from __future__ import annotations

from app.core.agent_enums import (
    AgentAssetContentType,
    AgentAssetDomain,
    AgentAssetStatus,
    AgentAssetType,
    AgentName,
)
from app.models.agent_asset import AgentAsset
from app.services.agent_asset_access import platform_asset_statement
from app.services.agent_foundation_constants import (
    DIGITAL_EMPLOYEE_ALGORITHM_REPLAY_TASK_CODE,
    DIGITAL_EMPLOYEE_BUDGET_PRECONTROL_TASK_CODE,
    DIGITAL_EMPLOYEE_DEPARTMENT_BASELINE_TASK_CODE,
    DIGITAL_EMPLOYEE_FALSE_POSITIVE_SAMPLE_TASK_CODE,
    DIGITAL_EMPLOYEE_FEEDBACK_SAMPLE_TASK_CODE,
    DIGITAL_EMPLOYEE_FINANCE_DASHBOARD_SNAPSHOT_TASK_CODE,
    DIGITAL_EMPLOYEE_FINANCE_REPORT_TASK_CODE,
    DIGITAL_EMPLOYEE_MULTI_EVIDENCE_TASK_CODE,
    DIGITAL_EMPLOYEE_POLICY_ALIGNMENT_TASK_CODE,
    DIGITAL_EMPLOYEE_POLICY_CLAUSE_EXTRACT_TASK_CODE,
    DIGITAL_EMPLOYEE_POLICY_GAP_TASK_CODE,
    DIGITAL_EMPLOYEE_PROFILE_SCAN_TASK_CODE,
    DIGITAL_EMPLOYEE_REMINDER_SCAN_TASK_CODE,
    DIGITAL_EMPLOYEE_RISK_GRAPH_SCAN_TASK_CODE,
    DIGITAL_EMPLOYEE_RULE_DISCOVERY_TASK_CODE,
    DIGITAL_EMPLOYEE_RULE_TEMPLATE_ORGANIZE_TASK_CODE,
    DIGITAL_EMPLOYEE_SKILL_CATEGORIES,
    DIGITAL_EMPLOYEE_SPATIOTEMPORAL_TASK_CODE,
    DIGITAL_EMPLOYEE_SUPPLIER_PROFILE_TASK_CODE,
    DIGITAL_EMPLOYEE_SUPPLIER_RELATION_TASK_CODE,
)

DIGITAL_EMPLOYEE_ANALYSIS_ROLE_BOUNDARY = (
    "规则由人定义，风险由人确认，主流程由外层智能体执行，"
    "数字员工只读取事实、规则命中和反馈结果，生成后台分析、报告和待复核材料。"
)


class AgentFoundationDigitalEmployeeTaskMixin:
    def _runtime_digital_employee_task_specs(self) -> tuple[dict[str, object], ...]:
        return (
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_FINANCE_DASHBOARD_SNAPSHOT_TASK_CODE,
                name="财务经营快照沉淀",
                description="按固定周期统计报销金额、费用结构、预算占用、高额单据和个人费用排行，刷新财务看板缓存。",
                scenario_json=["schedule", "finance_dashboard", "expense", "budget"],
                owner="财务运营组",
                cron="0 2 * * *",
                skill_category="整理",
                skill_name="finance-dashboard-snapshot-analyst",
                output_format="finance_dashboard_snapshot",
                input_sources=[
                    "expense_claims",
                    "expense_items",
                    "budget_snapshots",
                    "employee_profiles",
                ],
                execution_strategy="scheduled_dashboard_cache",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_REMINDER_SCAN_TASK_CODE,
                name="定时提醒与待办扫描",
                description="按计划扫描待审批单据、预算编制周期、差旅申请到期和逾期报销，生成可触达的提醒事项。",
                scenario_json=["schedule", "reminder", "approval", "budget", "travel"],
                owner="财务运营组",
                cron="0 2 * * *",
                skill_category="升级",
                skill_name="digital-employee-reminder-scanner",
                output_format="digital_employee_reminder_report",
                input_sources=[
                    "expense_claims",
                    "approval_tasks",
                    "budgets",
                    "travel_applications",
                ],
                execution_strategy="scheduled_reminder_scan",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_FINANCE_REPORT_TASK_CODE,
                name="财务报告编排与邮件投递",
                description=(
                    "按周、季、年整合费用、预算、风险、画像和提醒结果，"
                    "生成图文 PDF 报告并按邮箱设置投递给财务管理人员。"
                ),
                scenario_json=["schedule", "finance_report", "pdf", "email", "management"],
                owner="财务运营组",
                cron="30 8 * * 1",
                skill_category="整理",
                skill_name="finance-report-orchestrator",
                output_format="finance_report_pdf_delivery",
                input_sources=[
                    "finance_dashboard_snapshots",
                    "budget_snapshots",
                    "risk_observations",
                    "employee_profiles",
                    "digital_employee_reminders",
                    "system_mail_settings",
                ],
                execution_strategy="scheduled_pdf_email_report",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_POLICY_CLAUSE_EXTRACT_TASK_CODE,
                name="制度条款结构化抽取",
                description="按计划从财务制度和报销政策中抽取适用范围、限制条件、金额标准、审批要求和证据字段。",
                scenario_json=["schedule", "knowledge", "policy_clause", "ontology"],
                owner="财务制度管理组",
                cron="15 3 * * *",
                skill_category="整理",
                skill_name="finance-policy-clause-extractor",
                output_format="policy_clause_structuring_report",
                input_sources=["finance_policies", "knowledge_documents", "ontology_parse_logs"],
                execution_strategy="definition_ready",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_POLICY_ALIGNMENT_TASK_CODE,
                name="报销政策口径对齐",
                description="对齐不同制度、规则中心和知识库中的报销口径，发现同义、冲突、缺失和过期条款。",
                scenario_json=["schedule", "knowledge", "expense_policy", "rule_center"],
                owner="财务制度管理组",
                cron="30 3 * * *",
                skill_category="整理",
                skill_name="expense-policy-alignment",
                output_format="policy_alignment_report",
                input_sources=["finance_policies", "risk_rules", "knowledge_items"],
                execution_strategy="definition_ready",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_RULE_TEMPLATE_ORGANIZE_TASK_CODE,
                name="规则命中样本整理",
                description="把外层智能体流程已经产生的规则命中、制度引用和历史样本整理为字段映射与复核材料，不新增、不改写、不发布规则。",
                scenario_json=["schedule", "rule_hit", "risk_rule", "policy_ref"],
                owner="风控与审计部",
                cron="45 3 * * 1",
                skill_category="整理",
                skill_name="rule-execution-case-organizer",
                output_format="rule_hit_sample_pack",
                input_sources=["approved_risk_rules", "policy_refs", "rule_hits"],
                execution_strategy="definition_ready",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_DEPARTMENT_BASELINE_TASK_CODE,
                name="部门费用基线沉淀",
                description="按部门、费用类型和时间窗口沉淀费用基线，为预算柔性控制和同类对比提供长期参照。",
                scenario_json=["schedule", "department", "baseline", "expense"],
                owner="风控与审计部",
                cron="45 8 * * 1",
                skill_category="积累",
                skill_name="department-expense-baseline-accumulator",
                output_format="department_expense_baseline_snapshot",
                input_sources=["expense_claims", "expense_items", "profile_baselines"],
                execution_strategy="reuse_employee_profile_baseline",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_SUPPLIER_PROFILE_TASK_CODE,
                name="供应商风险画像沉淀",
                description="沉淀供应商、商户、酒店和收款方的费用频次、金额分布、异常关系和历史风险反馈。",
                scenario_json=["schedule", "supplier", "baseline", "risk_graph"],
                owner="风控与审计部",
                cron="0 8 * * 2",
                skill_category="积累",
                skill_name="supplier-risk-profile-accumulator",
                output_format="supplier_risk_profile_snapshot",
                input_sources=["expense_claims", "invoice_entities", "risk_observations"],
                execution_strategy="reuse_profile_baseline",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_FALSE_POSITIVE_SAMPLE_TASK_CODE,
                name="历史误报样本沉淀",
                description="归集被人工标记为误报、忽略或撤销的风险观察，形成算法回放和人工复核校准样本。",
                scenario_json=["schedule", "false_positive", "feedback", "replay"],
                owner="风控与审计部",
                cron="20 10 * * 1",
                skill_category="积累",
                skill_name="false-positive-sample-accumulator",
                output_format="false_positive_sample_pool",
                input_sources=["risk_observations", "risk_observation_feedback"],
                execution_strategy="definition_ready",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_FEEDBACK_SAMPLE_TASK_CODE,
                name="风险观察反馈样本沉淀",
                description="归集确认、补件、升级、改写和人工复核反馈，形成风险观察反馈样本池。",
                scenario_json=["schedule", "feedback", "risk_observation", "sample_pool"],
                owner="风控与审计部",
                cron="40 10 * * 1",
                skill_category="积累",
                skill_name="risk-feedback-sample-accumulator",
                output_format="risk_feedback_sample_pool",
                input_sources=["risk_observations", "risk_observation_feedback", "agent_runs"],
                execution_strategy="definition_ready",
            ),
            {
                "code": DIGITAL_EMPLOYEE_RISK_GRAPH_SCAN_TASK_CODE,
                "name": "财务风险图谱巡检",
                "description": (
                    "按计划扫描报销单、票据、审批链、员工画像和规则命中结果，"
                    "生成风险观察与可复核证据链。"
                ),
                "scenario_json": ["schedule", "expense", "risk_graph", "risk_observation"],
                "owner": "风控与审计部",
                "reviewer": "顾承宇",
                "cron": "0 9 * * *",
                "skill_category": "评估",
                "markdown": self._financial_risk_graph_scan_skill_markdown,
                "change_note": "初始化财务风险图谱巡检能力。",
                "config": {
                    "skill_name": "financial-risk-graph-scanner",
                    "scan_scope": [
                        "expense_claims",
                        "invoices",
                        "approval_chain",
                        "employee_profiles",
                        "risk_rules",
                    ],
                    "output_format": "risk_observation_report",
                    "writes_risk_observations": True,
                    "allowed_outputs": [
                        "facts",
                        "rule_hits",
                        "risk_clues",
                        "evidence_refs",
                        "human_review_required",
                    ],
                    "role_boundary": DIGITAL_EMPLOYEE_ANALYSIS_ROLE_BOUNDARY,
                    "writes_rules": False,
                },
            },
            {
                "code": DIGITAL_EMPLOYEE_PROFILE_SCAN_TASK_CODE,
                "name": "员工行为画像巡检",
                "description": (
                    "按计划更新员工费用行为、材料完整性、审批效率和智能协作画像，"
                    "为风险图谱提供画像基线。"
                ),
                "scenario_json": ["schedule", "employee_profile", "baseline", "risk_graph"],
                "owner": "风控与审计部",
                "reviewer": "顾承宇",
                "cron": "30 8 * * 1",
                "skill_category": "积累",
                "markdown": self._employee_behavior_profile_scan_skill_markdown,
                "change_note": "初始化员工行为画像巡检能力。",
                "config": {
                    "skill_name": "employee-behavior-profile-scanner",
                    "profile_dimensions": [
                        "expense_intensity",
                        "material_completeness",
                        "approval_efficiency",
                        "ai_collaboration",
                    ],
                    "output_format": "employee_behavior_profile_snapshot",
                    "writes_profile_snapshots": True,
                    "allowed_outputs": [
                        "facts",
                        "profile_snapshots",
                        "baseline_metrics",
                        "evidence_refs",
                        "human_review_required",
                    ],
                    "role_boundary": DIGITAL_EMPLOYEE_ANALYSIS_ROLE_BOUNDARY,
                    "writes_rules": False,
                },
            },
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_MULTI_EVIDENCE_TASK_CODE,
                name="单据多凭证一致性评估",
                description="比对报销单、费用明细、发票、流水、合同和事前申请之间的金额、数量、主体和时间字段。",
                scenario_json=["schedule", "expense", "multi_evidence", "risk_observation"],
                owner="风控与审计部",
                cron="15 9 * * *",
                skill_category="评估",
                skill_name="multi-evidence-consistency-evaluator",
                output_format="multi_evidence_consistency_report",
                input_sources=["expense_claims", "expense_items", "invoices", "attachments"],
                execution_strategy="reuse_financial_risk_graph_scan",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_SPATIOTEMPORAL_TASK_CODE,
                name="差旅时空一致性评估",
                description="评估差旅发生时间、提交时间、票据地点、消费地点、行程轨迹和开票地点是否一致。",
                scenario_json=["schedule", "travel", "spatiotemporal", "risk_observation"],
                owner="风控与审计部",
                cron="30 9 * * *",
                skill_category="评估",
                skill_name="travel-spatiotemporal-consistency-evaluator",
                output_format="spatiotemporal_consistency_report",
                input_sources=[
                    "expense_claims",
                    "expense_items",
                    "invoice_locations",
                    "travel_routes",
                ],
                execution_strategy="reuse_financial_risk_graph_scan",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_BUDGET_PRECONTROL_TASK_CODE,
                name="预算占用与超标预警",
                description="评估预算占用、费用标准、历史基线和柔性控制边界，输出提交前或审批前预警建议。",
                scenario_json=["schedule", "budget", "expense", "precontrol"],
                owner="预算管理组",
                cron="45 9 * * *",
                skill_category="评估",
                skill_name="budget-overrun-precontrol-evaluator",
                output_format="budget_precontrol_warning_report",
                input_sources=[
                    "expense_claims",
                    "budget_snapshots",
                    "policy_refs",
                    "profile_baselines",
                ],
                execution_strategy="definition_ready",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_SUPPLIER_RELATION_TASK_CODE,
                name="供应商异常关系评估",
                description="识别员工、部门、供应商、票据和报销单之间的异常聚集、重复关系和跨部门集中风险。",
                scenario_json=["schedule", "supplier", "risk_graph", "relationship"],
                owner="风控与审计部",
                cron="0 9 * * 2",
                skill_category="评估",
                skill_name="supplier-abnormal-relation-evaluator",
                output_format="supplier_abnormal_relation_report",
                input_sources=[
                    "risk_graph",
                    "expense_claims",
                    "invoice_entities",
                    "entity_registry",
                ],
                execution_strategy="reuse_financial_risk_graph_scan",
            ),
            {
                "code": DIGITAL_EMPLOYEE_RULE_DISCOVERY_TASK_CODE,
                "name": "风险线索归集",
                "description": (
                    "按计划复盘申请、报销、规则命中和人工反馈，"
                    "归集带事实依据的潜在线索，提交人工复核，不生成规则。"
                ),
                "scenario_json": ["schedule", "application", "reimbursement", "risk_clue"],
                "owner": "风控与审计部",
                "reviewer": "顾承宇",
                "cron": "0 10 * * 1",
                "skill_category": "升级",
                "markdown": self._risk_clue_collector_skill_markdown,
                "change_note": "初始化风险线索归集能力。",
                "config": {
                    "task_type": "risk_clue_collect",
                    "skill_name": "risk-clue-collector",
                    "input_sources": [
                        "expense_applications",
                        "expense_claims",
                        "rule_hits",
                        "risk_observation_feedback",
                    ],
                    "output_format": "risk_clue_review_packet",
                    "allowed_outputs": [
                        "facts",
                        "rule_hits",
                        "risk_clues",
                        "evidence_refs",
                        "human_review_required",
                    ],
                    "role_boundary": DIGITAL_EMPLOYEE_ANALYSIS_ROLE_BOUNDARY,
                    "writes_rules": False,
                    "human_review_required": True,
                },
            },
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_ALGORITHM_REPLAY_TASK_CODE,
                name="风险算法回放评测",
                description="复跑历史风险观察、反馈标签、本体版本和规则版本，评估算法升级前后的误报率和确认率。",
                scenario_json=["schedule", "algorithm_replay", "evaluation", "feedback"],
                owner="风控与审计部",
                cron="30 10 * * 1",
                skill_category="升级",
                skill_name="risk-algorithm-replay-evaluator",
                output_format="algorithm_replay_evaluation_report",
                input_sources=[
                    "algorithm_replay_sets",
                    "risk_observations",
                    "risk_observation_feedback",
                ],
                execution_strategy="definition_ready",
            ),
            self._digital_employee_task_spec(
                code=DIGITAL_EMPLOYEE_POLICY_GAP_TASK_CODE,
                name="制度引用缺口提示",
                description="整理申请、报销、规则命中和人工反馈中缺少制度引用的事实位置，提示人工补齐制度依据，不输出规则变更建议。",
                scenario_json=["schedule", "policy_reference", "evidence_gap", "human_review"],
                owner="财务制度管理组",
                cron="0 11 * * 1",
                skill_category="升级",
                skill_name="policy-reference-gap-hinter",
                output_format="policy_reference_gap_hint_report",
                input_sources=[
                    "policy_refs",
                    "rule_hits",
                    "expense_claims",
                    "risk_feedback_samples",
                ],
                execution_strategy="definition_ready",
            ),
        )

    def _digital_employee_task_spec(
        self,
        *,
        code: str,
        name: str,
        description: str,
        scenario_json: list[str],
        owner: str,
        cron: str,
        skill_category: str,
        skill_name: str,
        output_format: str,
        input_sources: list[str],
        execution_strategy: str,
    ) -> dict[str, object]:
        return {
            "code": code,
            "name": name,
            "description": description,
            "scenario_json": scenario_json,
            "owner": owner,
            "reviewer": "顾承宇",
            "cron": cron,
            "skill_category": skill_category,
            "markdown": lambda: self._generic_digital_employee_skill_markdown(
                skill_name=skill_name,
                title=name,
                description=description,
            ),
            "change_note": f"初始化{name}能力。",
            "config": {
                "skill_name": skill_name,
                "input_sources": input_sources,
                "output_format": output_format,
                "writes_work_record": True,
                "execution_strategy": execution_strategy,
                "allowed_outputs": [
                    "facts",
                    "rule_hits",
                    "risk_clues",
                    "evidence_refs",
                    "human_review_required",
                ],
                "role_boundary": DIGITAL_EMPLOYEE_ANALYSIS_ROLE_BOUNDARY,
                "writes_rules": False,
            },
        }

    def _generic_digital_employee_skill_markdown(
        self,
        *,
        skill_name: str,
        title: str,
        description: str,
    ) -> str:
        return self._read_domain_skill_markdown(
            skill_name,
            [
                "---",
                f"name: {skill_name}",
                f"description: {description}",
                "---",
                "",
                f"# {title}",
                "",
                "## 功能说明",
                "",
                description,
            ],
        )

    def _upsert_runtime_digital_employee_tasks(self, existing_codes: set[str]) -> None:
        for spec in self._runtime_digital_employee_task_specs():
            self._upsert_runtime_digital_employee_task(existing_codes, spec)

    def _upsert_runtime_digital_employee_task(
        self,
        existing_codes: set[str],
        spec: dict[str, object],
    ) -> None:
        code = str(spec["code"])
        config = self._build_runtime_digital_employee_config(spec)

        if code not in existing_codes:
            asset = self._create_seed_asset(
                asset_type=AgentAssetType.TASK.value,
                code=code,
                name=str(spec["name"]),
                description=str(spec["description"]),
                domain=AgentAssetDomain.SYSTEM.value,
                scenario_json=list(spec["scenario_json"]),
                owner=str(spec["owner"]),
                reviewer=str(spec["reviewer"]),
                status=AgentAssetStatus.ACTIVE.value,
                current_version="v1.0.0",
                config_json=config,
            )
        else:
            asset = self.db.scalar(platform_asset_statement().where(AgentAsset.code == code))
            if asset is None:
                return
            self._refresh_runtime_digital_employee_asset(asset, spec)

        markdown_builder = spec["markdown"]
        if not callable(markdown_builder):
            return
        self._ensure_asset_version(
            asset,
            version="v1.0.0",
            content=markdown_builder(),
            content_type=AgentAssetContentType.MARKDOWN.value,
            change_note=str(spec["change_note"]),
            created_by="系统初始化",
        )

    def _build_runtime_digital_employee_config(
        self,
        spec: dict[str, object],
        *,
        existing_config: dict[str, object] | None = None,
    ) -> dict[str, object]:
        code = str(spec["code"])
        cron = str(spec["cron"])
        base = {
            **self._digital_employee_task_config(code, cron),
            "skill_category": str(spec["skill_category"]),
            "schedule": cron,
            "cron_expression": cron,
            **dict(spec["config"]),
        }
        if not existing_config:
            return base

        existing_cron = (
            existing_config.get("cron")
            or existing_config.get("schedule")
            or existing_config.get("cron_expression")
        )
        schedule_config = (
            {"cron": existing_cron, "schedule": existing_cron, "cron_expression": existing_cron}
            if existing_cron
            else {}
        )
        return {
            **existing_config,
            "agent": AgentName.HERMES.value,
            "task_type": code.replace("task.hermes.", "").replace(".", "_"),
            "skill_category": str(spec["skill_category"]),
            "skill_category_options": list(DIGITAL_EMPLOYEE_SKILL_CATEGORIES),
            **dict(spec["config"]),
            **schedule_config,
        }

    def _refresh_runtime_digital_employee_asset(
        self,
        asset: AgentAsset,
        spec: dict[str, object],
    ) -> None:
        asset.name = str(spec["name"])
        asset.description = str(spec["description"])
        asset.owner = str(spec["owner"])
        asset.reviewer = str(spec["reviewer"])
        asset.domain = AgentAssetDomain.SYSTEM.value
        asset.scenario_json = list(spec["scenario_json"])
        if not str(asset.status or "").strip():
            asset.status = AgentAssetStatus.ACTIVE.value
        if not str(asset.current_version or "").strip():
            asset.current_version = "v1.0.0"
        if not str(asset.working_version or "").strip():
            asset.working_version = asset.current_version

        asset.config_json = self._build_runtime_digital_employee_config(
            spec,
            existing_config=dict(asset.config_json or {}),
        )
        self.db.add(asset)
