"""Tenant posting policy for automatic GL documents (intl. controls)."""
from __future__ import annotations

from uuid import UUID

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.foundation import AccountingSettings

# Defaults follow conservative international practice:
# balanced vouchers, immutable posted docs, sequential numbers, source refs,
# idempotent re-post prevention. Auto-post modules default OFF until enabled.
DEFAULT_POLICY: dict[str, str] = {
    "auto_post.sales.invoice": "false",
    "auto_post.sales.return": "false",
    "auto_post.purchase.goods_receipt": "false",
    "auto_post.purchase.invoice": "false",
    "auto_post.purchase.return": "true",
    "auto_post.treasury.receipt": "true",
    "auto_post.treasury.payment": "true",
    "auto_post.treasury.transfer": "true",
    "auto_post.inventory.issue": "true",
    "auto_post.payroll": "false",
    "auto_post.assets.depreciation": "false",
    "policy.require_balanced": "true",
    "policy.require_source_reference": "true",
    "policy.lock_after_post": "true",
    "policy.sequential_numbers": "true",
    "policy.idempotent_source": "true",
    "policy.posting_mode": "direct_post",  # direct_post | draft_then_post
}

BOOL_TRUE = {"1", "true", "yes", "on"}


def _as_bool(value: str | None, default: bool = False) -> bool:
    if value is None:
        return default
    return value.strip().lower() in BOOL_TRUE


class PostingPolicyService:
    PREFIX = "posting."

    def __init__(self, session: AsyncSession) -> None:
        self.session = session

    async def get_policy(self, tenant_id: UUID) -> dict:
        stmt = select(AccountingSettings).where(
            AccountingSettings.tenant_id == tenant_id,
            AccountingSettings.key.like(f"{self.PREFIX}%"),
        )
        rows = list((await self.session.execute(stmt)).scalars().all())
        stored = {r.key[len(self.PREFIX) :]: (r.value or "") for r in rows}
        merged = {**DEFAULT_POLICY, **stored}

        auto_post = {
            "sales_invoice": _as_bool(merged.get("auto_post.sales.invoice")),
            "sales_return": _as_bool(merged.get("auto_post.sales.return")),
            "purchase_goods_receipt": _as_bool(merged.get("auto_post.purchase.goods_receipt")),
            "purchase_invoice": _as_bool(merged.get("auto_post.purchase.invoice")),
            "purchase_return": _as_bool(merged.get("auto_post.purchase.return")),
            "treasury_receipt": _as_bool(merged.get("auto_post.treasury.receipt"), True),
            "treasury_payment": _as_bool(merged.get("auto_post.treasury.payment"), True),
            "treasury_transfer": _as_bool(merged.get("auto_post.treasury.transfer"), True),
            "inventory_issue": _as_bool(merged.get("auto_post.inventory.issue"), True),
            "payroll": _as_bool(merged.get("auto_post.payroll")),
            "assets_depreciation": _as_bool(merged.get("auto_post.assets.depreciation")),
        }
        return {
            "auto_post": auto_post,
            "require_balanced": _as_bool(merged.get("policy.require_balanced"), True),
            "require_source_reference": _as_bool(
                merged.get("policy.require_source_reference"), True
            ),
            "lock_after_post": _as_bool(merged.get("policy.lock_after_post"), True),
            "sequential_numbers": _as_bool(merged.get("policy.sequential_numbers"), True),
            "idempotent_source": _as_bool(merged.get("policy.idempotent_source"), True),
            "posting_mode": merged.get("policy.posting_mode") or "direct_post",
            "raw": merged,
        }

    async def update_policy(self, tenant_id: UUID, body: dict) -> dict:
        updates: dict[str, str] = {}
        auto = body.get("auto_post") or {}
        mapping = {
            "sales_invoice": "auto_post.sales.invoice",
            "sales_return": "auto_post.sales.return",
            "purchase_goods_receipt": "auto_post.purchase.goods_receipt",
            "purchase_invoice": "auto_post.purchase.invoice",
            "purchase_return": "auto_post.purchase.return",
            "treasury_receipt": "auto_post.treasury.receipt",
            "treasury_payment": "auto_post.treasury.payment",
            "treasury_transfer": "auto_post.treasury.transfer",
            "inventory_issue": "auto_post.inventory.issue",
            "payroll": "auto_post.payroll",
            "assets_depreciation": "auto_post.assets.depreciation",
        }
        for fe_key, store_key in mapping.items():
            if fe_key in auto:
                updates[store_key] = "true" if bool(auto[fe_key]) else "false"

        for key in (
            "require_balanced",
            "require_source_reference",
            "lock_after_post",
            "sequential_numbers",
            "idempotent_source",
        ):
            if key in body:
                updates[f"policy.{key}"] = "true" if bool(body[key]) else "false"
        if "posting_mode" in body and body["posting_mode"] in ("direct_post", "draft_then_post"):
            updates["policy.posting_mode"] = str(body["posting_mode"])

        for short_key, value in updates.items():
            full_key = f"{self.PREFIX}{short_key}"
            stmt = select(AccountingSettings).where(
                AccountingSettings.tenant_id == tenant_id,
                AccountingSettings.key == full_key,
            )
            row = (await self.session.execute(stmt)).scalar_one_or_none()
            if row is None:
                row = AccountingSettings(
                    tenant_id=tenant_id,
                    key=full_key,
                    value=value,
                    value_type="boolean" if short_key.startswith("auto_post") or short_key.startswith("policy.") and short_key != "policy.posting_mode" else "string",
                )
                self.session.add(row)
            else:
                row.value = value
        await self.session.flush()
        return await self.get_policy(tenant_id)

    def is_auto_post_enabled(self, policy: dict, module: str, doc_type: str) -> bool:
        auto = policy.get("auto_post") or {}
        key = f"{module}.{doc_type}".replace("-", "_")
        aliases = {
            "sales.invoice": "sales_invoice",
            "sales.return": "sales_return",
            "purchase.goods_receipt": "purchase_goods_receipt",
            "purchase.invoice": "purchase_invoice",
            "purchase.return": "purchase_return",
            "treasury.receipt": "treasury_receipt",
            "treasury.payment": "treasury_payment",
            "treasury.transfer": "treasury_transfer",
            "inventory.issue": "inventory_issue",
            "payroll.payroll": "payroll",
            "assets.depreciation": "assets_depreciation",
        }
        mapped = aliases.get(key) or aliases.get(f"{module}.{doc_type}")
        if mapped:
            return bool(auto.get(mapped))
        # snake variants
        snake = key.replace(".", "_")
        return bool(auto.get(snake))
