from __future__ import annotations

from dataclasses import asdict, dataclass
from datetime import UTC, date, datetime, timedelta
from decimal import Decimal
from typing import Any, Literal
from zoneinfo import ZoneInfo

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.models.agent_run import AgentRun
from app.models.employee_behavior_profile import EmployeeBehaviorProfileSnapshot
from app.models.risk_observation import RiskObservation
from app.services.finance_dashboard import FinanceDashboardService
from app.services.finance_report_tenant import require_report_tenant_id

FinanceReportType = Literal["weekly", "quarterly", "annual"]


@dataclass(frozen=True, slots=True)
class FinanceReportPeriod:
    report_type: FinanceReportType
    start_date: date
    end_date: date
    label: str
    title: str

    def to_dict(self) -> dict[str, Any]:
        payload = asdict(self)
        payload["start_date"] = self.start_date.isoformat()
        payload["end_date"] = self.end_date.isoformat()
        return payload


class FinanceReportContextService:
    def __init__(self, db: Session, *, tenant_id: str) -> None:
        self.db = db
        self.tenant_id = require_report_tenant_id(tenant_id)

    def build_context(
        self,
        *,
        report_type: FinanceReportType = "weekly",
        start_date: date | None = None,
        end_date: date | None = None,
        now: datetime | None = None,
    ) -> dict[str, Any]:
        generated_at = now or datetime.now(UTC)
        period = self.resolve_period(
            report_type=report_type,
            start_date=start_date,
            end_date=end_date,
            now=generated_at,
        )
        dashboard = FinanceDashboardService(
            self.db,
            tenant_id=self.tenant_id,
        ).build_dashboard(
            range_key="自定义",
            start_date=period.start_date,
            end_date=period.end_date,
            trend_range="近12天" if report_type == "weekly" else "近30天",
            department_range="本季度" if report_type != "weekly" else "本月",
        )
        dashboard_payload = dashboard.model_dump(mode="json")
        risk_summary = self._risk_summary(period)
        profile_summary = self._profile_summary(period)
        digital_employee_summary = self._digital_employee_summary(period)
        actions = self._action_items(dashboard_payload, risk_summary)
        insights = self._insights(dashboard_payload, risk_summary, profile_summary, actions)

        return {
            "tenant_id": self.tenant_id,
            "report_type": report_type,
            "period": period.to_dict(),
            "generated_at": generated_at.isoformat(),
            "dashboard": dashboard_payload,
            "risk_summary": risk_summary,
            "profile_summary": profile_summary,
            "digital_employee_summary": digital_employee_summary,
            "insights": insights,
            "action_items": actions,
            "summary": self._summary(dashboard_payload, risk_summary, actions),
        }

    @staticmethod
    def resolve_period(
        *,
        report_type: FinanceReportType,
        start_date: date | None,
        end_date: date | None,
        now: datetime,
    ) -> FinanceReportPeriod:
        if start_date and end_date:
            begin = min(start_date, end_date)
            finish = max(start_date, end_date)
            return FinanceReportPeriod(
                report_type=report_type,
                start_date=begin,
                end_date=finish,
                label=f"{begin.isoformat()} 至 {finish.isoformat()}",
                title=_report_title(report_type),
            )

        local_now = now.astimezone(ZoneInfo("Asia/Shanghai")) if now.tzinfo else now
        today = local_now.date()
        if report_type == "annual":
            year = today.year - 1
            begin = date(year, 1, 1)
            finish = date(year, 12, 31)
            label = f"{year} 年"
        elif report_type == "quarterly":
            current_quarter = (today.month - 1) // 3 + 1
            year = today.year
            quarter = current_quarter - 1
            if quarter <= 0:
                quarter = 4
                year -= 1
            month = (quarter - 1) * 3 + 1
            begin = date(year, month, 1)
            finish = _month_end(year, month + 2)
            label = f"{year} 年 Q{quarter}"
        else:
            current_week_start = today - timedelta(days=today.weekday())
            begin = current_week_start - timedelta(days=7)
            finish = current_week_start - timedelta(days=1)
            label = f"{begin.isoformat()} 至 {finish.isoformat()}"

        return FinanceReportPeriod(
            report_type=report_type,
            start_date=begin,
            end_date=finish,
            label=label,
            title=_report_title(report_type),
        )

    def _risk_summary(self, period: FinanceReportPeriod) -> dict[str, Any]:
        start_dt = _day_start(period.start_date)
        end_dt = _day_after(period.end_date)
        rows = list(
            self.db.scalars(
                select(RiskObservation).where(
                    RiskObservation.tenant_id == self.tenant_id,
                    RiskObservation.created_at >= start_dt,
                    RiskObservation.created_at < end_dt,
                )
            ).all()
        )
        high_rows = [row for row in rows if str(row.risk_level or "").lower() == "high"]
        pending_rows = [
            row for row in rows if str(row.status or "").lower() in {"pending_review", "open"}
        ]
        top_signals: dict[str, int] = {}
        for row in rows:
            label = str(row.title or row.risk_signal or "风险观察").strip()
            top_signals[label] = top_signals.get(label, 0) + 1
        return {
            "risk_count": len(rows),
            "high_risk_count": len(high_rows),
            "pending_review_count": len(pending_rows),
            "top_signals": [
                {"name": name, "count": count}
                for name, count in sorted(
                    top_signals.items(),
                    key=lambda item: item[1],
                    reverse=True,
                )[:5]
            ],
        }

    def _profile_summary(self, period: FinanceReportPeriod) -> dict[str, Any]:
        start_dt = _day_start(period.start_date)
        end_dt = _day_after(period.end_date)
        rows = list(
            self.db.scalars(
                select(EmployeeBehaviorProfileSnapshot).where(
                    EmployeeBehaviorProfileSnapshot.tenant_id == self.tenant_id,
                    EmployeeBehaviorProfileSnapshot.calculated_at >= start_dt,
                    EmployeeBehaviorProfileSnapshot.calculated_at < end_dt,
                )
            ).all()
        )
        attention_rows = [
            row
            for row in rows
            if str(row.profile_level or "").lower() in {"attention", "high", "warning"}
            or int(row.profile_score or 0) >= 80
        ]
        return {
            "snapshot_count": len(rows),
            "attention_profile_count": len(attention_rows),
            "top_profiles": [
                {
                    "name": row.subject_name,
                    "department": row.department_name or "",
                    "score": row.profile_score,
                    "level": row.profile_level,
                }
                for row in sorted(
                    rows,
                    key=lambda item: int(item.profile_score or 0),
                    reverse=True,
                )[:5]
            ],
        }

    def _digital_employee_summary(self, period: FinanceReportPeriod) -> dict[str, Any]:
        start_dt = _day_start(period.start_date)
        end_dt = _day_after(period.end_date)
        rows = list(
            self.db.scalars(
                select(AgentRun).where(
                    AgentRun.route_json["tenant_id"].as_string() == self.tenant_id,
                    AgentRun.ontology_json["tenant_id"].as_string() == self.tenant_id,
                    AgentRun.agent == "hermes",
                    AgentRun.started_at >= start_dt,
                    AgentRun.started_at < end_dt,
                )
            ).all()
        )
        succeeded = [row for row in rows if row.status == "succeeded"]
        reports = [
            row
            for row in rows
            if "finance_report" in str((row.route_json or {}).get("task_type") or "")
        ]
        return {
            "run_count": len(rows),
            "succeeded_count": len(succeeded),
            "report_count": len(reports),
        }

    def _action_items(
        self,
        dashboard: dict[str, Any],
        risk: dict[str, Any],
    ) -> list[dict[str, Any]]:
        actions: list[dict[str, Any]] = []
        for item in list(dashboard.get("bottlenecks") or [])[:4]:
            name = str(item.get("name") or "财务关注项").strip()
            tone = str(item.get("tone") or "neutral").strip()
            if tone in {"warning", "danger"}:
                actions.append(
                    {
                        "title": name,
                        "owner": str(item.get("role") or "财务运营组"),
                        "priority": "high" if tone == "danger" else "medium",
                        "suggestion": (
                            f"请跟进{name}：{item.get('duration') or ''} {item.get('status') or ''}"
                        ).strip(),
                    }
                )
        if int(risk.get("pending_review_count") or 0) > 0:
            actions.append(
                {
                    "title": "风险观察待复核",
                    "owner": "风控与审计部",
                    "priority": "high",
                    "suggestion": f"当前有 {risk['pending_review_count']} 条风险观察待复核。",
                }
            )
        return actions[:6]

    def _insights(
        self,
        dashboard: dict[str, Any],
        risk: dict[str, Any],
        profile: dict[str, Any],
        actions: list[dict[str, Any]],
    ) -> list[str]:
        totals = dashboard.get("totals") if isinstance(dashboard.get("totals"), dict) else {}
        amount = float(totals.get("reimbursementAmount") or 0)
        count = int(totals.get("reimbursementCount") or 0)
        budget_rate = float(totals.get("budgetUsageRate") or 0)
        insights = [
            f"本周期报销 {count} 单，费用金额 {_money(amount)}。",
            f"预算使用率 {budget_rate:.1f}%，需关注预算预警和预占释放。",
            (
                f"风险观察 {risk.get('risk_count', 0)} 条，"
                f"其中高风险 {risk.get('high_risk_count', 0)} 条。"
            ),
        ]
        if int(profile.get("attention_profile_count") or 0) > 0:
            insights.append(f"员工画像中有 {profile['attention_profile_count']} 个高关注样本。")
        if actions:
            insights.append(f"数字员工整理出 {len(actions)} 项管理动作，建议纳入本周跟进。")
        return insights[:5]

    @staticmethod
    def _summary(
        dashboard: dict[str, Any],
        risk: dict[str, Any],
        actions: list[dict[str, Any]],
    ) -> dict[str, Any]:
        totals = dashboard.get("totals") if isinstance(dashboard.get("totals"), dict) else {}
        return {
            "reimbursement_count": int(totals.get("reimbursementCount") or 0),
            "reimbursement_amount": float(totals.get("reimbursementAmount") or 0),
            "pending_payment_amount": float(totals.get("pendingPaymentAmount") or 0),
            "risk_count": int(risk.get("risk_count") or 0),
            "action_count": len(actions),
        }


def _report_title(report_type: str) -> str:
    return {
        "weekly": "财务经营周报",
        "quarterly": "财务经营季报",
        "annual": "财务经营年报",
    }.get(report_type, "财务经营报告")


def _month_end(year: int, month: int) -> date:
    next_month = date(year + (month // 12), (month % 12) + 1, 1)
    return next_month - timedelta(days=1)


def _day_start(value: date) -> datetime:
    return datetime.combine(value, datetime.min.time(), tzinfo=UTC)


def _day_after(value: date) -> datetime:
    return datetime.combine(value + timedelta(days=1), datetime.min.time(), tzinfo=UTC)


def _money(value: float | Decimal) -> str:
    return f"¥{float(value):,.0f}"
