"""
Huella de contenido (fingerprint) para invalidar el PDF de reporte DODA.
"""

from __future__ import annotations

import hashlib
import json
from datetime import date, datetime
from decimal import Decimal
from typing import Any, Dict

from sqlalchemy import inspect as sa_inspect
from sqlalchemy.orm import Session

from .models import (
    Doda,
    DodaAmericanPedimento,
    DodaContainer,
    DodaContainerSeal,
    DodaPedimento,
)

_DODA_FINGERPRINT_EXCLUDE = frozenset(
    {
        "doda_report_pdf_path",
        "doda_report_pdf_generated_at",
        "doda_report_source_fingerprint",
        "created_at",
        "updated_at",
        "deleted_at",
    }
)


def _json_default(obj: Any) -> Any:
    if isinstance(obj, Decimal):
        return str(obj)
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, (bytes, bytearray)):
        return obj.hex()
    raise TypeError(f"Type {type(obj)} not serializable")


def _instance_payload(instance: object, *, exclude: frozenset[str]) -> Dict[str, Any]:
    insp = sa_inspect(instance)
    d: Dict[str, Any] = {}
    for col in insp.mapper.column_attrs:
        name = col.key
        if name in exclude or name in _DODA_FINGERPRINT_EXCLUDE:
            continue
        d[name] = getattr(instance, name)
    return d


def build_doda_fingerprint(db: Session, doda_id: int) -> str:
    doda = db.get(Doda, doda_id)
    if doda is None:
        raise ValueError("DODA no encontrado")

    doda_block = _instance_payload(doda, exclude=frozenset())

    containers_rows = (
        db.query(DodaContainer)
        .filter(DodaContainer.doda_id == doda_id)
        .order_by(DodaContainer.container_line.asc())
        .all()
    )
    container_blocks: list[Dict[str, Any]] = []
    for c in containers_rows:
        c_block = _instance_payload(
            c,
            exclude=frozenset(
                {
                    "id",
                    "doda_id",
                }
            ),
        )
        seals = (
            db.query(DodaContainerSeal)
            .filter(DodaContainerSeal.container_id == c.id)
            .order_by(DodaContainerSeal.seal_line.asc())
            .all()
        )
        c_block["seals"] = [
            _instance_payload(
                s,
                exclude=frozenset({"id", "container_id", "doda_id"}),
            )
            for s in seals
        ]
        container_blocks.append(c_block)

    american = (
        db.query(DodaAmericanPedimento)
        .filter(DodaAmericanPedimento.doda_id == doda_id)
        .order_by(DodaAmericanPedimento.american_pedimento_line.asc())
        .all()
    )
    american_blocks = [
        _instance_payload(
            p, exclude=frozenset({"id", "doda_id"})
        )
        for p in american
    ]

    pedimentos = (
        db.query(DodaPedimento)
        .filter(DodaPedimento.doda_id == doda_id)
        .order_by(DodaPedimento.pedimento_line.asc())
        .all()
    )
    pedimento_blocks = [
        _instance_payload(p, exclude=frozenset({"id", "doda_id"})) for p in pedimentos
    ]

    snapshot: Dict[str, Any] = {
        "doda": doda_block,
        "containers": container_blocks,
        "american_pedimentos": american_blocks,
        "pedimentos_detail": pedimento_blocks,
    }
    raw = json.dumps(
        snapshot,
        sort_keys=True,
        ensure_ascii=True,
        separators=(",", ":"),
        default=_json_default,
    )
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()
