"""
Generación de PDF de reporte DODA (Jinja2 + pdfkit / wkhtmltopdf).
"""

from __future__ import annotations

import json
import logging
import shutil
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any, List, Optional

import pdfkit
from jinja2 import Environment, FileSystemLoader, select_autoescape
from sqlalchemy.orm import Session

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

logger = logging.getLogger(__name__)


@dataclass
class _SealView:
    seal_line: int
    seal_value: str


@dataclass
class _ContainerView:
    container_line: int
    container_value: str
    seals: List[_SealView]


@dataclass
class _AmericanView:
    american_pedimento_line: int
    american_pedimento_type: str
    american_pedimento_value: str


@dataclass
class _PedimentoView:
    pedimento_line: int
    authorization_patent: str
    document: str
    shipment: str
    cove: str
    umc: str
    pedimento_type: str


def _s(v: Optional[str]) -> str:
    if v is None:
        return ""
    return str(v).strip()


class DodaReportPdfService:
    def __init__(self) -> None:
        self.template_dir = Path(__file__).parent / "templates"
        self.jinja_env = Environment(
            loader=FileSystemLoader(self.template_dir),
            autoescape=select_autoescape(["html", "xml"]),
        )
        self._tpl = self.jinja_env.get_template("doda_report.html")

    def _get_wkhtmltopdf_config(self):
        for path in (
            shutil.which("wkhtmltopdf"),
            "/usr/local/bin/wkhtmltopdf",
            "/usr/bin/wkhtmltopdf",
        ):
            if path:
                return pdfkit.configuration(wkhtmltopdf=path)
        raise RuntimeError("wkhtmltopdf binary not found.")

    @staticmethod
    def _doda_header_block(d: Doda) -> dict[str, str]:
        return {
            "integration_number": _s(d.integration_number),
            "patent": _s(d.patent),
            "dispatch_customs": _s(d.dispatch_customs),
            "customs_sections": _s(d.customs_sections),
            "caat": _s(d.caat),
            "transport_identification": _s(d.transport_identification),
            "fast_id": _s(d.fast_id),
            "operation_type": _s(d.operation_type),
            "status": _s(d.status),
            "transaction_number": _s(d.transaction_number),
        }

    def _load_children(self, db: Session, doda_id: int) -> dict[str, Any]:
        containers = (
            db.query(DodaContainer)
            .filter(DodaContainer.doda_id == doda_id)
            .order_by(DodaContainer.container_line.asc())
            .all()
        )
        cviews: list[_ContainerView] = []
        for c in containers:
            seals = (
                db.query(DodaContainerSeal)
                .filter(DodaContainerSeal.container_id == c.id)
                .order_by(DodaContainerSeal.seal_line.asc())
                .all()
            )
            cviews.append(
                _ContainerView(
                    container_line=c.container_line,
                    container_value=_s(c.container_value),
                    seals=[
                        _SealView(
                            seal_line=s.seal_line, seal_value=_s(s.seal_value)
                        )
                        for s in seals
                    ],
                )
            )

        american = (
            db.query(DodaAmericanPedimento)
            .filter(DodaAmericanPedimento.doda_id == doda_id)
            .order_by(DodaAmericanPedimento.american_pedimento_line.asc())
            .all()
        )
        amer_views = [
            _AmericanView(
                american_pedimento_line=p.american_pedimento_line,
                american_pedimento_type=_s(p.american_pedimento_type),
                american_pedimento_value=_s(p.american_pedimento_value),
            )
            for p in american
        ]

        peds = (
            db.query(DodaPedimento)
            .filter(DodaPedimento.doda_id == doda_id)
            .order_by(DodaPedimento.pedimento_line.asc())
            .all()
        )
        ped_views = [
            _PedimentoView(
                pedimento_line=p.pedimento_line,
                authorization_patent=_s(p.authorization_patent),
                document=_s(p.document),
                shipment=_s(p.shipment),
                cove=_s(p.cove),
                umc=_s(p.umc),
                pedimento_type=_s(p.pedimento_type),
            )
            for p in peds
        ]

        return {
            "containers": [asdict(x) for x in cviews],
            "american_pedimentos": [asdict(x) for x in amer_views],
            "pedimentos_detail": [asdict(x) for x in ped_views],
        }

    def build_context(self, db: Session, doda: Doda) -> dict[str, Any]:
        fp = build_doda_fingerprint(db, doda.id)
        children = self._load_children(db, doda.id)
        return {
            "doda": self._doda_header_block(doda),
            "linq_sat_qr": _s(d.linq_sat_qr),
            "sat_chain_preview": _s((d.sat_original_chain or d.original_chain) or "")[:2000],
            "sat_digital_seal_preview": _s(d.sat_digital_seal)[:2000],
            "fingerprint_sha256": fp,
            **children,
        }

    def render_pdf_bytes(self, context: dict[str, Any]) -> bytes:
        html = self._tpl.render(**context)
        options = {
            "page-size": "A4",
            "encoding": "UTF-8",
            "margin-top": "12mm",
            "margin-bottom": "12mm",
            "margin-left": "10mm",
            "margin-right": "10mm",
        }
        return pdfkit.from_string(
            html,
            False,
            options=options,
            configuration=self._get_wkhtmltopdf_config(),
        )

    def build_pdf_for_doda(self, db: Session, doda: Doda) -> bytes:
        ctx = self.build_context(db, doda)
        return self.render_pdf_bytes(ctx)

    @staticmethod
    def debug_json_snapshot(context: dict[str, Any]) -> str:
        """Para depuración: snapshot legible (no contiene el sello completo)."""
        return json.dumps(context, ensure_ascii=True, indent=2)
