"""
Exportación de listado DODA a CSV / TSV (xls) / pipe, alineada al reporte legacy GDoda.
"""

from __future__ import annotations

import csv
import io
from datetime import date, datetime
from enum import Enum
from typing import Any, List, Optional

from sqlalchemy import and_
from sqlalchemy.orm import Session

from .models import Doda, DodaPedimento

# Encabezados (orden legacy Clarion)
EXPORT_HEADERS: List[str] = [
    "SYSID",
    "NUM INTEGRACIÓN",
    "FECHA",
    "HORA",
    "ADUANA",
    "ADUANA ES",
    "PATENTE",
    "PEDIMENTOS",
    "CAAT",
    "IDEN. TRANSPORTE",
    "FAST_ID",
    "TIPO OPERACIÓN",
    "RESPONSABLE",
    "TRANSPORTISTA",
    "REMESAS",
    "TIPO PEDIMENTO",
    "CADENA ORIGINIAL",
    "NUMERO SERIE",
    "FIRMA ELECTRÓNICA",
    "NO TRANSACCIÓN",
    "ESTATUS",
    "LINQSQTQR",
    "SAT_CERTIFICADO",
    "SELLO DIGITAL",
    "PATH XML ENVÍO",
    "PATH XML RESPUESTA",
    "SAT_CADENA ORIGINAL",
    "DESPACHO ADUANERO",
    "GAFETE ÚNICO",
    "USUARIO",
]


class DodaExportFormat(str, Enum):
    csv = "csv"
    xls = "xls"
    txt = "txt"


def _parse_iso_date(s: str) -> date:
    s = (s or "").strip()
    for fmt in ("%Y-%m-%d", "%Y/%m/%d", "%d/%m/%Y", "%d-%m-%Y"):
        try:
            return datetime.strptime(s, fmt).date()
        except ValueError:
            continue
    raise ValueError(f"Fecha inválida: {s!r} (use YYYY-MM-DD)")


def _date_to_yyyymmdd(d: date) -> int:
    return d.year * 10000 + d.month * 100 + d.day


def _format_doda_date_formatted(doda_date: Optional[int]) -> str:
    if doda_date is None:
        return ""
    s = str(doda_date)
    if len(s) == 8 and s.isdigit():
        y, m, d = s[:4], s[4:6], s[6:8]
        return f"{d}/{m}/{y}"
    return s


def _format_doda_time(doda_time: Optional[int]) -> str:
    if doda_time is None:
        return ""
    t = int(doda_time)
    s = str(t)
    if len(s) <= 2:
        return s
    if len(s) == 4:
        return f"{s[:2]}:{s[2:4]}"
    if len(s) == 6:
        return f"{s[:2]}:{s[2:4]}:{s[4:6]}"
    if len(s) > 6:
        return s[:2] + ":" + s[2:4] + ":" + s[4:6]
    return s


def _as_text(value: Any) -> str:
    if value is None:
        return ""
    if isinstance(value, bool):
        return "1" if value else "0"
    s = str(value)
    s = s.replace("\r\n", " ").replace("\n", " ").replace("\r", " ")
    return s


def doda_row_values(
    row: Doda, *, date_mode: str
) -> List[str]:
    """date_mode: 'raw' | 'formatted' (legacy FechaJul branch)."""
    if date_mode == "raw":
        fecha = _as_text(row.doda_date)
        hora = _as_text(row.doda_time)
    else:
        fecha = _format_doda_date_formatted(row.doda_date)
        hora = _format_doda_time(row.doda_time)

    return [
        _as_text(row.id),
        _as_text(row.integration_number),
        fecha,
        hora,
        _as_text(row.dispatch_customs),
        _as_text(row.customs_sections),
        _as_text(row.patent),
        _as_text(row.pedimentos),
        _as_text(row.caat),
        _as_text(row.transport_identification),
        _as_text(row.fast_id),
        _as_text(row.operation_type),
        _as_text(row.responsible),
        _as_text(row.carrier),
        _as_text(row.shipments),
        _as_text(row.pedimento_type),
        _as_text(row.original_chain),
        _as_text(row.serial_number),
        _as_text(row.electronic_signature),
        _as_text(row.transaction_number),
        _as_text(row.status),
        _as_text(row.linq_sat_qr),
        _as_text(row.sat_certificate),
        _as_text(row.sat_digital_seal),
        _as_text(row.xml_doda_sent_path),
        _as_text(row.xml_doda_response_path),
        _as_text(row.sat_original_chain),
        _as_text(row.customs_clearance),
        _as_text(row.unique_badge_number),
        _as_text(row.last_user),
    ]


def _delimiter_for_format(fmt: DodaExportFormat) -> str:
    if fmt == DodaExportFormat.csv:
        return ","
    if fmt == DodaExportFormat.xls:
        return "\t"
    if fmt == DodaExportFormat.txt:
        return "|"
    return ","


def _content_type_and_filename(fmt: DodaExportFormat) -> tuple[str, str]:
    if fmt == DodaExportFormat.csv:
        return "text/csv; charset=utf-8", "doda_export.csv"
    if fmt == DodaExportFormat.xls:
        return "application/vnd.ms-excel; charset=utf-8", "doda_export.xls"
    return "text/plain; charset=utf-8", "doda_export.txt"


def list_dodas_in_date_range(
    db: Session,
    *,
    tenant_id: int,
    company_id: int,
    date_start: int,
    date_end: int,
) -> List[Doda]:
    return (
        db.query(Doda)
        .filter(
            and_(
                Doda.tenant_id == tenant_id,
                Doda.company_id == company_id,
                Doda.doda_date.isnot(None),
                Doda.doda_date >= date_start,
                Doda.doda_date <= date_end,
            )
        )
        .order_by(Doda.doda_date.asc(), Doda.id.asc())
        .all()
    )


def build_export_text(
    rows: List[Doda],
    *,
    export_format: DodaExportFormat,
    date_mode: str = "formatted",
) -> str:
    delim = _delimiter_for_format(export_format)
    out = io.StringIO()
    w = csv.writer(
        out,
        delimiter=delim,
        quoting=csv.QUOTE_MINIMAL,
        lineterminator="\r\n",
    )
    w.writerow(EXPORT_HEADERS)
    for r in rows:
        w.writerow(doda_row_values(r, date_mode=date_mode))
    return out.getvalue()


def parse_export_params(
    date_from: str,
    date_to: str,
    format_str: str,
    date_mode: str,
) -> tuple[int, int, DodaExportFormat, str]:
    d0 = _date_to_yyyymmdd(_parse_iso_date(date_from))
    d1 = _date_to_yyyymmdd(_parse_iso_date(date_to))
    if d0 > d1:
        raise ValueError("date_from no puede ser posterior a date_to")
    try:
        fmt = DodaExportFormat(format_str.lower().strip())
    except ValueError:
        raise ValueError("format debe ser csv, xls o txt")
    mode = (date_mode or "formatted").lower().strip()
    if mode not in ("raw", "formatted"):
        raise ValueError("date_mode debe ser raw o formatted")
    return d0, d1, fmt, mode


# --- Exportación de pedimentos (líneas) de un DODA específico (legacy / pantalla) ---

PEDIMENTO_EXPORT_HEADERS: List[str] = [
    "PATENTE",
    "DOCUMENTO",
    "ACUSE_VA",
    "REMESA",
    "CANTIDAD",
    "IMPORTE_USD",
    "IMPORTE_DIF_USD",
    "NIU",
    "ARTICULO",
]


def _as_decimal_text(value: Any) -> str:
    if value is None:
        return ""
    return _as_text(value)


def pedimento_row_values(row: DodaPedimento) -> List[str]:
    """
    ACUSE_VA = COVE; CANTIDAD = UMC (captura típica en listado);
    IMPORTE_USD / IMPORTE_DIF_USD = montos en USD; ARTICULO = art. 7 (0/1).
    """
    return [
        _as_text(row.authorization_patent),
        _as_text(row.document),
        _as_text(row.cove),
        _as_text(row.shipment),
        _as_text(row.umc),
        _as_decimal_text(row.effective_amount_usd),
        _as_decimal_text(row.difference_amount_usd),
        _as_text(row.dta_niu),
        _as_text(row.article_7),
    ]


def list_pedimentos_for_doda_export(
    db: Session,
    *,
    tenant_id: int,
    company_id: int,
    doda_id: int,
) -> List[DodaPedimento]:
    return (
        db.query(DodaPedimento)
        .join(Doda, DodaPedimento.doda_id == Doda.id)
        .filter(
            Doda.id == doda_id,
            Doda.tenant_id == tenant_id,
            Doda.company_id == company_id,
        )
        .order_by(DodaPedimento.pedimento_line.asc())
        .all()
    )


def build_pedimentos_export_text(
    rows: List[DodaPedimento],
    *,
    export_format: DodaExportFormat,
) -> str:
    delim = _delimiter_for_format(export_format)
    out = io.StringIO()
    w = csv.writer(
        out,
        delimiter=delim,
        quoting=csv.QUOTE_MINIMAL,
        lineterminator="\r\n",
    )
    w.writerow(PEDIMENTO_EXPORT_HEADERS)
    for r in rows:
        w.writerow(pedimento_row_values(r))
    return out.getvalue()


def parse_pedimento_export_format(format_str: str) -> DodaExportFormat:
    try:
        return DodaExportFormat(format_str.lower().strip())
    except ValueError as e:
        raise ValueError("format debe ser csv, xls o txt") from e
