"""Idempotent source-document posting guard (intl. no double-post)."""
from __future__ import annotations

from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.models.posting import PostingReference
from app.repositories.posting import PostingReferenceRepository
from app.services.posting_policy_service import PostingPolicyService
from shared.exceptions import AppError


class SourcePostingGuard:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.ref_repo = PostingReferenceRepository(session)
        self.policy_svc = PostingPolicyService(session)

    async def assert_can_post(
        self,
        tenant_id: UUID,
        *,
        source_module: str,
        source_document_type: str,
        source_document_id: str,
    ) -> None:
        policy = await self.policy_svc.get_policy(tenant_id)
        if not policy.get("idempotent_source", True):
            return
        existing = await self.ref_repo.get_by_source(
            tenant_id, source_module, source_document_type, source_document_id
        )
        if existing is not None and existing.is_posted:
            raise AppError(
                "این سند مبدأ قبلاً در دفتر کل ثبت شده است (جلوگیری از ثبت تکراری).",
                status_code=409,
                error_code="source_already_posted",
            )

    async def mark_posted(
        self,
        tenant_id: UUID,
        *,
        voucher_id: UUID,
        source_module: str,
        source_document_type: str,
        source_document_id: str,
    ) -> PostingReference:
        existing = await self.ref_repo.get_by_source(
            tenant_id, source_module, source_document_type, source_document_id
        )
        if existing is None:
            ref = PostingReference(
                tenant_id=tenant_id,
                voucher_id=voucher_id,
                source_module=source_module,
                source_document_type=source_document_type,
                source_document_id=source_document_id,
                is_posted=True,
            )
            return await self.ref_repo.add(ref)
        existing.voucher_id = voucher_id
        existing.is_posted = True
        await self.session.flush()
        return existing

    async def should_post_immediately(self, tenant_id: UUID) -> bool:
        policy = await self.policy_svc.get_policy(tenant_id)
        return (policy.get("posting_mode") or "direct_post") != "draft_then_post"
