"""规则版本来源解析器。"""

from __future__ import annotations

import hashlib
from dataclasses import dataclass
from pathlib import Path

from fastapi_common.fastapi_common_logger import logger
from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
from fastapi_common.fastapi_common_storage.oss_client import OssClient
from sqlalchemy import text


@dataclass(frozen=True)
class RuleVersionPayload:
    """规则文件解析结果。"""

    localPath: str
    sourceType: str
    sourcePath: str
    ruleVersionId: int | None = None
    ruleTypeId: str | None = None
    fileSha256: str | None = None


class RuleVersionResolver:
    """按运行记录解析规则 YAML 文件来源。"""

    def __init__(self, Oss: OssClient | None = None) -> None:
        self.Oss = Oss or OssClient()

    async def ResolveForRun(self, RunId: int) -> RuleVersionPayload | None:
        """根据运行记录解析规则文件来源。"""
        RunInfo = await self._LoadRunInfo(RunId)
        if not RunInfo:
            return None

        LocalCachePath = RunInfo["rule_local_cache_path"]
        if LocalCachePath:
            CachePath = Path(LocalCachePath)
            if CachePath.is_file():
                return RuleVersionPayload(
                    localPath=str(CachePath),
                    sourceType="local_cache",
                    sourcePath=str(CachePath),
                    ruleVersionId=RunInfo["rule_version_id"],
                    ruleTypeId=RunInfo["rule_type_id"],
                    fileSha256=RunInfo["rule_source_sha256"],
                )

        SourceUrl = RunInfo["rule_source_oss_url"]
        if not SourceUrl:
            return None

        return await self._DownloadFromUrl(
            Url=SourceUrl,
            RuleVersionId=RunInfo["rule_version_id"],
            RuleTypeId=RunInfo["rule_type_id"],
            ExpectedSha256=RunInfo["rule_source_sha256"],
        )

    async def _LoadRunInfo(self, RunId: int) -> dict[str, object] | None:
        """读取运行记录中的规则来源信息。"""
        async with GetAsyncSession() as Session:
            Result = await Session.execute(
                text(
                    """
                    SELECT
                        rule_version_id,
                        rule_type_id,
                        rule_source_oss_url,
                        rule_source_sha256,
                        rule_local_cache_path
                    FROM leaudit_audit_runs
                    WHERE id = :run_id
                    LIMIT 1
                    """
                ),
                {"run_id": RunId},
            )
            Row = Result.mappings().first()
            return dict(Row) if Row else None

    async def _DownloadFromUrl(
        self,
        *,
        Url: str,
        RuleVersionId: int | None,
        RuleTypeId: str | None,
        ExpectedSha256: str | None,
    ) -> RuleVersionPayload:
        """从 OSS 下载规则 YAML 到本地临时文件。"""
        try:
            Content = self.Oss.DownloadBytes(Url)
        except Exception as Error:
            logger.error(f"下载规则 YAML 失败: url={Url}, error={Error}")
            raise

        ActualSha256 = hashlib.sha256(Content).hexdigest()
        if ExpectedSha256 and ActualSha256.lower() != ExpectedSha256.lower():
            raise ValueError(
                "规则 YAML SHA256 校验失败: "
                f"expected={ExpectedSha256}, actual={ActualSha256}"
            )

        FilePrefix = "leaudit-rule-"
        if RuleTypeId:
            SafeTypeId = RuleTypeId.replace("/", "_").replace(".", "_")
            FilePrefix = f"{FilePrefix}{SafeTypeId}-"

        LocalPath = self.Oss.WriteTempBytes(
            Content=Content,
            Suffix=".yaml",
            Prefix=FilePrefix,
        )

        return RuleVersionPayload(
            localPath=LocalPath,
            sourceType="oss",
            sourcePath=Url,
            ruleVersionId=RuleVersionId,
            ruleTypeId=RuleTypeId,
            fileSha256=ActualSha256,
        )
