from __future__ import annotations

from datetime import UTC, date, datetime
from time import perf_counter
from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.core.agent_enums import (
    AgentName,
    AgentPermissionLevel,
    AgentRunSource,
    AgentRunStatus,
    AgentToolType,
)
from app.models.agent_run import AgentRun
from app.services.agent_runs import AgentRunService
from app.services.commercial_runtime_policy import tenant_from_agent_route
from app.services.finance_report_context import FinanceReportContextService, FinanceReportType
from app.services.finance_report_mailer import FinanceReportMailer
from app.services.finance_report_renderer import FinanceReportRenderer
from app.services.finance_report_tenant import (
    TenantFinanceReportRunService,
    require_report_tenant_id,
)

FINANCE_REPORT_TASK_TYPE = "finance_report_orchestration"
FINANCE_REPORT_TOOL_NAME = "digital_employee.finance_report.orchestrate"


class DigitalEmployeeFinanceReportTaskService:
    def __init__(self, db: Session) -> None:
        self.db = db

    def generate_report(
        self,
        *,
        report_type: FinanceReportType = "weekly",
        start_date: date | None = None,
        end_date: date | None = None,
        recipients: list[str] | None = None,
        send_email: bool = True,
        dry_run_email: bool = False,
        source: str = AgentRunSource.SCHEDULE.value,
        run_id: str | None = None,
        record_tool_call: bool = True,
        tenant_id: str = "default",
    ) -> dict[str, Any]:
        tenant = require_report_tenant_id(tenant_id)
        if run_id is not None:
            self._require_run_tenant(run_id=run_id, tenant_id=tenant)
        period = FinanceReportContextService.resolve_period(
            report_type=report_type,
            start_date=start_date,
            end_date=end_date,
            now=datetime.now(UTC),
        )
        ledger_service = TenantFinanceReportRunService(self.db)
        ledger, acquired = ledger_service.reserve(
            tenant_id=tenant,
            report_type=report_type,
            period_start=period.start_date,
            period_end=period.end_date,
        )
        if not acquired:
            existing = dict(ledger.result_json or {})
            if existing:
                return {**existing, "idempotent_replay": True}
            return {
                "task_type": FINANCE_REPORT_TASK_TYPE,
                "tenant_id": tenant,
                "report_type": report_type,
                "status": "already_running",
                "period": period.to_dict(),
                "idempotent_replay": True,
            }

        run_service = AgentRunService(self.db)
        run = None
        try:
            if run_id is None:
                run = run_service.create_run(
                    agent=AgentName.HERMES.value,
                    source=source,
                    tenant_id=tenant,
                    user_id="digital_employee",
                    ontology_json={"scenario": "finance_report", "intent": report_type},
                    route_json={
                        "task_type": FINANCE_REPORT_TASK_TYPE,
                        "report_type": report_type,
                        "report_period_key": ledger.idempotency_key,
                        "phase": "running",
                        "heartbeat_at": datetime.now(UTC).isoformat(),
                    },
                    permission_level=AgentPermissionLevel.READ.value,
                    status=AgentRunStatus.RUNNING.value,
                )
                run_id = run.run_id
            ledger_service.attach_agent_run(ledger, run_id)
        except Exception as exc:
            ledger_service.fail(ledger, exc)
            raise

        timer = perf_counter()
        try:
            context = FinanceReportContextService(
                self.db,
                tenant_id=tenant,
            ).build_context(
                report_type=report_type,
                start_date=start_date,
                end_date=end_date,
            )
            rendered = FinanceReportRenderer().render(context)
            delivery = (
                FinanceReportMailer(self.db).send_report(
                    context=context,
                    pdf_path=rendered.pdf_path,
                    tenant_id=tenant,
                    recipients=recipients,
                    dry_run=dry_run_email,
                )
                if send_email
                else None
            )
            duration_ms = int((perf_counter() - timer) * 1000)
            result = self._result_payload(
                context=context,
                rendered=rendered,
                delivery=delivery.to_dict() if delivery is not None else {"status": "skipped"},
            )
            if record_tool_call:
                run_service.record_tool_call(
                    run_id=run_id,
                    tool_type=AgentToolType.DATABASE.value,
                    tool_name=FINANCE_REPORT_TOOL_NAME,
                    request_json={
                        "task_type": FINANCE_REPORT_TASK_TYPE,
                        "report_type": report_type,
                        "tenant_id": tenant,
                        "send_email": send_email,
                    },
                    response_json=result,
                    status=AgentRunStatus.SUCCEEDED.value,
                    duration_ms=duration_ms,
                )
            run_service.merge_route_json(
                run_id,
                {
                    "phase": "succeeded",
                    "task_type": FINANCE_REPORT_TASK_TYPE,
                    "report_type": report_type,
                    "report_period_key": ledger.idempotency_key,
                    "report_delivery": result,
                    "heartbeat_at": datetime.now(UTC).isoformat(),
                },
                status=AgentRunStatus.SUCCEEDED.value,
                result_summary=self._summary_text(result),
                finished_at=datetime.now(UTC),
            )
            ledger_service.succeed(ledger, result)
            return result
        except Exception as exc:
            ledger_service.fail(ledger, exc)
            if run_id:
                run_service.merge_route_json(
                    run_id,
                    {
                        "phase": "failed",
                        "task_type": FINANCE_REPORT_TASK_TYPE,
                        "report_type": report_type,
                        "report_period_key": ledger.idempotency_key,
                        "heartbeat_at": datetime.now(UTC).isoformat(),
                    },
                    status=AgentRunStatus.FAILED.value,
                    error_message=str(exc),
                    finished_at=datetime.now(UTC),
                )
            raise

    @staticmethod
    def _result_payload(
        *,
        context: dict[str, Any],
        rendered: Any,
        delivery: dict[str, Any],
    ) -> dict[str, Any]:
        period = context.get("period") if isinstance(context.get("period"), dict) else {}
        summary = context.get("summary") if isinstance(context.get("summary"), dict) else {}
        return {
            "task_type": FINANCE_REPORT_TASK_TYPE,
            "tenant_id": context.get("tenant_id"),
            "report_type": context.get("report_type"),
            "title": period.get("title"),
            "period": period,
            "summary": summary,
            "insights": context.get("insights") or [],
            "action_items": context.get("action_items") or [],
            "pdf": {
                "storage_key": rendered.storage_key,
                "path": str(rendered.pdf_path),
                "html_path": str(rendered.html_path),
                "page_count": rendered.page_count,
            },
            "delivery": delivery,
        }

    def _require_run_tenant(self, *, run_id: str, tenant_id: str) -> None:
        run = self.db.scalar(select(AgentRun).where(AgentRun.run_id == run_id))
        if run is None or tenant_from_agent_route(run.route_json) != tenant_id:
            raise LookupError("财务报告运行不存在或不属于当前租户。")

    @staticmethod
    def _summary_text(result: dict[str, Any]) -> str:
        summary = result.get("summary") if isinstance(result.get("summary"), dict) else {}
        delivery = result.get("delivery") if isinstance(result.get("delivery"), dict) else {}
        return (
            f"{result.get('title') or '财务经营报告'}已生成："
            f"{summary.get('reimbursement_count', 0)} 单，"
            f"金额 {float(summary.get('reimbursement_amount') or 0):,.0f} 元，"
            f"邮件状态 {delivery.get('status') or 'skipped'}。"
        )
