"""站点写操作应用用例。"""

from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Mapping, Optional, Protocol

from app.application.subscription.delete import AsyncUnitOfWork


@dataclass(frozen=True, slots=True)
class SiteMutationResult:
    """描述站点写操作是否成功及兼容提示信息。"""

    success: bool
    message: str = ""


class SiteMutationRepository(Protocol):
    """站点写用例需要的最小持久化端口。"""

    async def get_by_id(self, site_id: int) -> Optional[Any]:
        """读取指定站点。"""
        ...

    async def async_get_by_domain(self, domain: str) -> Optional[Any]:
        """按域名读取站点。"""
        ...

    async def stage_create(self, payload: Mapping[str, Any]) -> None:
        """暂存新增站点。"""
        ...

    async def stage_update(self, site_id: int, payload: Mapping[str, Any]) -> bool:
        """暂存站点更新并返回目标是否存在。"""
        ...

    async def stage_delete(self, site_id: int) -> None:
        """暂存站点删除。"""
        ...

    async def stage_priorities(self, priorities: list[dict]) -> None:
        """暂存一组站点优先级变更。"""
        ...

    async def stage_reset(self) -> None:
        """暂存清空全部站点。"""
        ...


SiteIndexerLoader = Callable[[str], Awaitable[Optional[dict]]]
SiteEventPublisher = Callable[[dict], Awaitable[None]]
DomainExtractor = Callable[[str], str]
UrlNormalizer = Callable[[str], str]


class SiteMutationCommand:
    """统一执行站点新增、更新、优先级和删除事务。"""

    def __init__(
            self,
            *,
            repository: SiteMutationRepository,
            unit_of_work: AsyncUnitOfWork,
            auth_level_provider: Callable[[], int],
            indexer_loader: SiteIndexerLoader,
            domain_extractor: DomainExtractor,
            url_normalizer: UrlNormalizer,
            publish_updated: SiteEventPublisher,
            publish_deleted: SiteEventPublisher,
    ) -> None:
        """保存站点验证、持久化、事务和提交后事件端口。"""
        self._repository = repository
        self._unit_of_work = unit_of_work
        self._auth_level_provider = auth_level_provider
        self._indexer_loader = indexer_loader
        self._domain_extractor = domain_extractor
        self._url_normalizer = url_normalizer
        self._publish_updated = publish_updated
        self._publish_deleted = publish_deleted

    async def create(self, payload: Mapping[str, Any]) -> SiteMutationResult:
        """校验并新增站点，提交成功后发布站点更新事件。"""
        values = dict(payload)
        raw_url = values.get("url")
        if not raw_url:
            return SiteMutationResult(False, "站点地址不能为空")
        if self._auth_level_provider() < 2:
            return SiteMutationResult(False, "用户未通过认证，无法使用站点功能！")

        domain = self._domain_extractor(raw_url)
        site_info = await self._indexer_loader(domain)
        if not site_info:
            return SiteMutationResult(False, "该站点不支持，请检查站点域名是否正确")
        if await self._repository.async_get_by_domain(domain):
            return SiteMutationResult(False, f"{domain} 站点己存在")

        values.update({
            "id": None,
            "domain": domain,
            "url": self._url_normalizer(raw_url),
            "name": site_info.get("name"),
            "public": 1 if site_info.get("public") else 0,
        })
        await self._repository.stage_create(values)
        await self._commit()
        await self._publish_updated({"domain": domain})
        return SiteMutationResult(True)

    async def update(self, payload: Mapping[str, Any]) -> SiteMutationResult:
        """更新站点并在提交后发布完整的兼容事件载荷。"""
        values = dict(payload)
        site_id = values.get("id")
        if not site_id or not await self._repository.get_by_id(site_id):
            return SiteMutationResult(False, "站点不存在")

        values["url"] = self._url_normalizer(values.get("url") or "")
        values["domain"] = self._domain_extractor(values["url"])
        await self._repository.stage_update(site_id, values)
        await self._commit()
        await self._publish_updated({
            "site_id": site_id,
            "domain": values["domain"],
            "name": values.get("name"),
            "site_url": values["url"],
        })
        return SiteMutationResult(True)

    async def update_priorities(self, priorities: list[dict]) -> SiteMutationResult:
        """在同一事务中更新全部站点优先级。"""
        await self._repository.stage_priorities(priorities)
        await self._commit()
        return SiteMutationResult(True)

    async def delete(self, site_id: int) -> SiteMutationResult:
        """删除站点，并确保删除事件只在提交成功后发送。"""
        await self._repository.stage_delete(site_id)
        await self._commit()
        await self._publish_deleted({"site_id": site_id})
        return SiteMutationResult(True)

    async def reset(self) -> SiteMutationResult:
        """清空全部站点，并在提交后发布通配站点删除事件。"""
        await self._repository.stage_reset()
        await self._commit()
        await self._publish_deleted({"site_id": "*"})
        return SiteMutationResult(True)

    async def _commit(self) -> None:
        """提交当前站点事务，失败时回滚并保留原始异常。"""
        try:
            await self._unit_of_work.commit()
        except Exception:
            await self._unit_of_work.rollback()
            raise
