from __future__ import annotations

from pathlib import Path

from sqlalchemy import select

from app.core.agent_enums import (
    AgentAssetStatus,
)
from app.core.logging import get_logger
from app.models.agent_asset import AgentAsset
from app.services.agent_asset_spreadsheet import (
    COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
    COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME,
    COMPANY_TRAVEL_EXPENSE_RULE_CODE,
    COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,
    FINANCE_RULES_LIBRARY,
    AgentAssetSpreadsheetManager,
)
from app.services.agent_foundation_constants import (
    COMPANY_COMMUNICATION_RULE_SCENARIO_JSON,
    COMPANY_TRAVEL_RULE_SCENARIO_JSON,
)
from app.services.finance_rule_catalog import (
    DEPRECATED_FINANCE_RULE_CODES,
    DEPRECATED_FINANCE_RULE_REPLACEMENTS,
)

logger = get_logger("app.services.agent_foundation")


class AgentFoundationSpreadsheetMixin:
    def sync_finance_rule_assets_from_catalog(self) -> int:
        synced_count = self._ensure_core_finance_rule_asset_metadata()
        self._hide_deprecated_finance_rule_assets()
        self.db.flush()
        return synced_count

    def _ensure_core_finance_rule_asset_metadata(self) -> int:
        synced_count = 0
        synced_count += int(
            self._ensure_core_finance_rule_asset(
                code=COMPANY_TRAVEL_EXPENSE_RULE_CODE,
                scenario_category=COMPANY_TRAVEL_RULE_SCENARIO_JSON[0],
                finance_rule_sheet="差旅住宿费标准",
                expense_types=["travel", "hotel", "transport"],
            )
        )
        synced_count += int(
            self._ensure_core_finance_rule_asset(
                code=COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
                scenario_category=COMPANY_COMMUNICATION_RULE_SCENARIO_JSON[0],
                finance_rule_sheet="通信费报销标准",
                expense_types=["communication"],
            )
        )
        return synced_count

    def _ensure_core_finance_rule_asset(
        self,
        *,
        code: str,
        scenario_category: str,
        finance_rule_sheet: str,
        expense_types: list[str],
    ) -> bool:
        asset = self.db.scalar(select(AgentAsset).where(AgentAsset.code == code))
        if asset is None:
            return False
        asset.scenario_json = [scenario_category]
        asset.config_json = {
            **(asset.config_json or {}),
            "enabled": True,
            "tag": "财务规则",
            "detail_mode": "spreadsheet",
            "rule_library": FINANCE_RULES_LIBRARY,
            "scenario_category": scenario_category,
            "ai_review_category": scenario_category,
            "finance_rule_code": code,
            "finance_rule_sheet": finance_rule_sheet,
            "expense_types": expense_types,
            "business_stage": ["expense_application", "reimbursement"],
            "budget_required": True,
        }
        return True

    def _hide_deprecated_finance_rule_assets(self) -> None:
        for code in DEPRECATED_FINANCE_RULE_CODES:
            asset = self.db.scalar(select(AgentAsset).where(AgentAsset.code == code))
            if asset is None:
                continue
            asset.status = AgentAssetStatus.DISABLED.value
            asset.scenario_json = ["已废弃"]
            replacement = DEPRECATED_FINANCE_RULE_REPLACEMENTS.get(code)
            deprecated_reason = (
                "交通/住宿细分并入公司差旅费报销规则，不再作为独立财务规则展示。"
                if replacement
                else (
                    "该费用类型没有独立职务金额分档，额度控制转入预算中心，"
                    "不再作为独立财务规则表展示。"
                )
            )
            asset.config_json = {
                **(asset.config_json or {}),
                "enabled": False,
                "tag": "废弃规则",
                "deprecated": True,
                "deprecated_reason": deprecated_reason,
            }
            if replacement:
                asset.config_json["replaced_by"] = replacement

    def _ensure_company_travel_rule_spreadsheet_seed(

        self,

        asset: AgentAsset,

        *,

        version: str,

        actor_name: str,

    ):

        manager = AgentAssetSpreadsheetManager()

        manager.ensure_rule_library_dirs()

        live_document = manager.store_rule_library_spreadsheet(

            library=FINANCE_RULES_LIBRARY,

            file_name=COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,

            content=self._read_or_build_company_travel_rule_file(manager),

            actor_name=actor_name,

            source="rule-library",

        )

        existing_document = (

            asset.config_json.get("rule_document")

            if isinstance(asset.config_json, dict)

            else None

        )

        storage_key = (

            str(existing_document.get("storage_key") or "").strip()

            if isinstance(existing_document, dict)

            else ""

        )

        if storage_key:

            try:

                existing_path = manager.resolve_storage_path(storage_key)

            except FileNotFoundError:

                existing_path = None

            if existing_path is not None and existing_path.exists():

                asset.config_json = {

                    **(asset.config_json or {}),

                    "detail_mode": "spreadsheet",

                    "tag": "财务规则",

                    "rule_library": FINANCE_RULES_LIBRARY,

                    "rule_document": {

                        **AgentAssetSpreadsheetManager.build_rule_document_config(

                            live_document,

                            asset_version=version,

                        ),

                        "storage_key": live_document.storage_key,

                    },

                }

                return live_document

        asset.config_json = {

            **(asset.config_json or {}),

            "detail_mode": "spreadsheet",

            "tag": "财务规则",

            "rule_library": FINANCE_RULES_LIBRARY,

            "rule_document": {

                **AgentAssetSpreadsheetManager.build_rule_document_config(

                    live_document,

                    asset_version=version,

                ),

                "storage_key": live_document.storage_key,

            },

        }

        return live_document

    def _ensure_company_communication_rule_spreadsheet_seed(

        self,

        asset: AgentAsset,

        *,

        version: str,

        actor_name: str,

    ):

        return self._ensure_finance_rule_spreadsheet_seed(

            asset,

            version=version,

            actor_name=actor_name,

            file_name=COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME,

            fallback_sheet_name="通信费报销规则",

        )

    @staticmethod

    def _read_or_build_company_travel_rule_file(

        manager: AgentAssetSpreadsheetManager,

    ) -> bytes:

        live_key = (

            Path("rules")

            / FINANCE_RULES_LIBRARY

            / COMPANY_TRAVEL_EXPENSE_RULE_FILENAME

        ).as_posix()

        live_path = manager.resolve_storage_path(live_key)

        if live_path.exists():

            return live_path.read_bytes()

        return AgentAssetSpreadsheetManager.build_blank_rule_workbook("差旅费报销规则")

    def _ensure_finance_rule_spreadsheet_seed(

        self,

        asset: AgentAsset,

        *,

        version: str,

        actor_name: str,

        file_name: str,

        fallback_sheet_name: str,

        workbook_sheets: list[tuple[str, list[list[object]]]] | None = None,

    ):

        manager = AgentAssetSpreadsheetManager()

        manager.ensure_rule_library_dirs()

        live_document = manager.store_rule_library_spreadsheet(

            library=FINANCE_RULES_LIBRARY,

            file_name=file_name,

            content=self._read_or_build_finance_rule_file(

                manager,

                file_name=file_name,

                fallback_sheet_name=fallback_sheet_name,

                workbook_sheets=workbook_sheets,

            ),

            actor_name=actor_name,

            source="rule-library",

        )

        existing_document = (

            asset.config_json.get("rule_document")

            if isinstance(asset.config_json, dict)

            else None

        )

        storage_key = (

            str(existing_document.get("storage_key") or "").strip()

            if isinstance(existing_document, dict)

            else ""

        )

        if storage_key:

            try:

                existing_path = manager.resolve_storage_path(storage_key)

            except FileNotFoundError:

                existing_path = None

            if existing_path is not None and existing_path.exists():

                asset.config_json = {

                    **(asset.config_json or {}),

                    "detail_mode": "spreadsheet",

                    "tag": "财务规则",

                    "rule_library": FINANCE_RULES_LIBRARY,

                    "rule_document": {

                        **AgentAssetSpreadsheetManager.build_rule_document_config(

                            live_document,

                            asset_version=version,

                        ),

                        "storage_key": live_document.storage_key,

                    },

                }

                return live_document

        asset.config_json = {

            **(asset.config_json or {}),

            "detail_mode": "spreadsheet",

            "tag": "财务规则",

            "rule_library": FINANCE_RULES_LIBRARY,

            "rule_document": {

                **AgentAssetSpreadsheetManager.build_rule_document_config(

                    live_document,

                    asset_version=version,

                ),

                "storage_key": live_document.storage_key,

            },

        }

        return live_document

    @staticmethod

    def _read_or_build_finance_rule_file(

        manager: AgentAssetSpreadsheetManager,

        *,

        file_name: str,

        fallback_sheet_name: str,

        workbook_sheets: list[tuple[str, list[list[object]]]] | None = None,

    ) -> bytes:

        live_key = (

            Path("rules")

            / FINANCE_RULES_LIBRARY

            / file_name

        ).as_posix()

        live_path = manager.resolve_storage_path(live_key)

        if live_path.exists():

            return live_path.read_bytes()

        if workbook_sheets is not None:

            return AgentAssetSpreadsheetManager.build_rule_workbook(workbook_sheets)

        return AgentAssetSpreadsheetManager.build_blank_rule_workbook(fallback_sheet_name)
