"""Agent 资产列表与版本响应的序列化辅助职责。"""

from __future__ import annotations

import json
from collections import defaultdict
from datetime import datetime
from typing import Any

from app.core.agent_enums import AgentAssetContentType, AgentAssetType, AgentReviewStatus
from app.models.agent_asset import AgentAsset, AgentAssetVersion
from app.schemas.agent_asset import AgentAssetListItem, AgentAssetVersionRead


class AgentAssetSerializationMixin:
    """集中处理资产/版本只读投影，不负责业务状态迁移。"""

    def _serialize_version(
        self, version: AgentAssetVersion, asset: AgentAsset
    ) -> AgentAssetVersionRead:
        latest_review = self.repository.get_review(asset.id, version.version)
        working_version = self._resolve_working_version(asset)
        published_version = self._resolve_published_version(asset)
        return AgentAssetVersionRead(
            id=version.id,
            tenant_id=version.tenant_id,
            scope=version.scope,
            asset_id=version.asset_id,
            version=version.version,
            content=self._deserialize_content(version),
            content_type=version.content_type,
            change_note=version.change_note,
            created_by=version.created_by,
            created_at=version.created_at,
            is_current=version.version == working_version,
            is_published=version.version == published_version,
            is_working=version.version == working_version,
            lifecycle_state=self._resolve_version_lifecycle_state(
                version.version,
                working_version=working_version,
                published_version=published_version,
                latest_review_status=latest_review.review_status if latest_review else "",
            ),
        )

    def _collect_version_stats(self, assets: list[AgentAsset]) -> dict[str, dict[str, Any]]:
        asset_ids = [item.id for item in assets]
        versions = self.repository.list_versions_for_assets(asset_ids)
        reviews = self.repository.list_reviews_for_assets(asset_ids)
        spreadsheet_logs = self.audit_service.repository.list_for_resources(
            resource_type=AgentAssetType.RULE.value,
            resource_ids=[
                item.id
                for item in assets
                if item.asset_type == AgentAssetType.RULE.value
                and str((item.config_json or {}).get("detail_mode") or "").strip().lower()
                == "spreadsheet"
            ],
            action="edit_rule_spreadsheet",
        )
        working_versions = {item.id: self._resolve_working_version(item) for item in assets}
        version_counts: dict[str, int] = defaultdict(int)
        modified_by: dict[str, str | None] = {item.id: None for item in assets}
        published_versions = {item.id: self._resolve_published_version(item) for item in assets}
        published_by: dict[str, str | None] = {}
        published_at: dict[str, datetime | None] = {}
        spreadsheet_edit_counts: dict[str, int] = defaultdict(int)
        spreadsheet_last_actor: dict[str, str | None] = {}
        spreadsheet_last_changed_at: dict[str, datetime] = {}

        for version in versions:
            version_counts[version.asset_id] += 1
            if modified_by.get(
                version.asset_id
            ) is None and version.version == working_versions.get(version.asset_id):
                modified_by[version.asset_id] = version.created_by

        for review in reviews:
            if review.asset_id in published_at:
                continue
            if review.version != published_versions.get(review.asset_id):
                continue
            if review.review_status != AgentReviewStatus.APPROVED.value:
                continue
            published_by[review.asset_id] = review.reviewer
            published_at[review.asset_id] = review.reviewed_at or review.created_at

        for log in spreadsheet_logs:
            spreadsheet_edit_counts[log.resource_id] += 1
            last_changed_at = spreadsheet_last_changed_at.get(log.resource_id)
            if last_changed_at is None or log.created_at >= last_changed_at:
                spreadsheet_last_changed_at[log.resource_id] = log.created_at
                spreadsheet_last_actor[log.resource_id] = log.actor

        return {
            item.id: {
                "change_count": (
                    spreadsheet_edit_counts.get(item.id, 0)
                    if item.asset_type == AgentAssetType.RULE.value
                    and str((item.config_json or {}).get("detail_mode") or "").strip().lower()
                    == "spreadsheet"
                    and spreadsheet_edit_counts.get(item.id, 0) > 0
                    else max(version_counts.get(item.id, 0) - 1, 0)
                ),
                "modified_by": (
                    spreadsheet_last_actor.get(item.id)
                    if item.asset_type == AgentAssetType.RULE.value
                    and str((item.config_json or {}).get("detail_mode") or "").strip().lower()
                    == "spreadsheet"
                    and spreadsheet_last_actor.get(item.id)
                    else modified_by.get(item.id)
                ),
                "published_by": published_by.get(item.id),
                "published_at": published_at.get(item.id),
            }
            for item in assets
        }

    @staticmethod
    def _serialize_list_item(
        asset: AgentAsset,
        version_stats: dict[str, int | str | None] | None = None,
    ) -> AgentAssetListItem:
        payload = AgentAssetListItem.model_validate(asset).model_dump()
        payload["change_count"] = int((version_stats or {}).get("change_count") or 0)
        payload["modified_by"] = str((version_stats or {}).get("modified_by") or "").strip() or None
        payload["published_by"] = (
            str((version_stats or {}).get("published_by") or "").strip() or None
        )
        payload["published_at"] = (version_stats or {}).get("published_at")
        return AgentAssetListItem.model_validate(payload)

    @staticmethod
    def _sort_versions(
        versions: list[AgentAssetVersion], current_version: str | None
    ) -> list[AgentAssetVersion]:
        return sorted(
            versions,
            key=lambda item: (item.version == current_version, item.created_at),
            reverse=True,
        )

    @staticmethod
    def _serialize_content(content: Any, content_type: str) -> str:
        if content_type == AgentAssetContentType.MARKDOWN.value:
            return str(content)
        return json.dumps(content, ensure_ascii=False, sort_keys=True, indent=2)

    @staticmethod
    def _deserialize_content(version: AgentAssetVersion | None) -> Any:
        if version is None:
            return None
        if version.content_type == AgentAssetContentType.MARKDOWN.value:
            return version.content
        return json.loads(version.content)

    @staticmethod
    def _increment_version(version: str | None) -> str:
        normalized = str(version or "").strip().removeprefix("v")
        parts = normalized.split(".")
        if len(parts) != 3 or not all(item.isdigit() for item in parts):
            return "v1.0.0"
        major, minor, patch = [int(item) for item in parts]
        return f"v{major}.{minor}.{patch + 1}"

    @staticmethod
    def _hash_bytes(content: bytes) -> str:
        import hashlib

        return hashlib.sha256(content).hexdigest()

    @staticmethod
    def _asset_snapshot(asset: AgentAsset) -> dict[str, Any]:
        return {
            "tenant_id": asset.tenant_id,
            "scope": asset.scope,
            "asset_type": asset.asset_type,
            "code": asset.code,
            "name": asset.name,
            "status": asset.status,
            "current_version": asset.current_version,
            "published_version": asset.published_version,
            "working_version": asset.working_version,
            "domain": asset.domain,
            "owner": asset.owner,
            "reviewer": asset.reviewer,
        }

    @staticmethod
    def _resolve_working_version(asset: AgentAsset) -> str:
        return str(asset.working_version or asset.current_version or "").strip()

    @staticmethod
    def _resolve_published_version(asset: AgentAsset) -> str:
        return str(asset.published_version or "").strip()

    @staticmethod
    def _resolve_version_lifecycle_state(
        version: str,
        *,
        working_version: str,
        published_version: str,
        latest_review_status: str,
    ) -> str:
        if version == published_version:
            return "published"
        if version != working_version:
            return "history"
        if latest_review_status == AgentReviewStatus.PENDING.value:
            return "pending_review"
        if latest_review_status == AgentReviewStatus.APPROVED.value:
            return "approved"
        if latest_review_status == AgentReviewStatus.REJECTED.value:
            return "rejected"
        return "draft"

    def _next_available_version(self, asset: AgentAsset) -> str:
        candidate = self._increment_version(self._resolve_working_version(asset))
        while self.repository.get_version(asset.id, candidate) is not None:
            candidate = self._increment_version(candidate)
        return candidate
