"""Lógica del Expediente: minteo del folio, avance de etapa y armado del timeline."""

from fastapi import HTTPException, status
from sqlalchemy.orm import Session

from ..common.folios import next_folio
from .models import Case

# Orden de etapas (solo se avanza, nunca retrocede)
STAGE_ORDER = ["oportunidad", "solicitud", "cotizacion", "operacion", "facturacion", "cerrado"]


def create_case(
    db: Session, tenant_id: int, company_id: int, *, account_id: int | None = None,
    title: str | None = None, stage: str = "oportunidad", user_id: str | None = None,
) -> Case:
    """Mintea un expediente con folio EXP... (sin commit; lo confirma quien lo invoca)."""
    case = Case(
        reference=next_folio(db, tenant_id, company_id, "EXP", None, with_direction=False),
        account_id=account_id, title=title, stage=stage, status="abierto",
        tenant_id=tenant_id, company_id=company_id, created_by=user_id, updated_by=user_id,
    )
    db.add(case)
    db.flush()

    # ── Carril hacia EFC (T2026-08-046) ──────────────────────────────────────────────────
    # EFC es la fuente única de los documentos del expediente. El reflejo allá —un pedimento
    # provisional— se pide AQUÍ, en el instante en que nace el folio, porque el folio es
    # precisamente la llave con la que las dos mitades se reconocen.
    #
    # El token de almacenamiento se fija al nacer y NO cambia nunca: es la carpeta de MinIO
    # del lado de EFC. Que sea inmutable es lo que permite completar el provisional con la
    # data aduanera real sin mover un solo archivo.
    #
    # Todo es best-effort y va en la MISMA transacción que el expediente:
    #   - con ``EFC_API_URL`` vacía es un no-op y el expediente vive igual, solo en el CRM;
    #   - si el encolado o el despacho fallan, no se propaga el error: el barrido del beat
    #     recoge lo pendiente. Un sistema de terceros caído no puede romper un alta.
    # El import es diferido para no acoplar el arranque del módulo del expediente al carril.
    from ..expediente_gateway import service as gateway
    from ..expediente_gateway.storage import storage_token

    case.efc_storage_token = storage_token(company_id, case.reference)
    db.flush()
    gateway.replicate_expediente_best_effort(db, case)
    return case


def advance_stage(db: Session, case_id: int | None, stage: str) -> None:
    """Avanza la etapa del expediente si la nueva es posterior a la actual."""
    if not case_id or stage not in STAGE_ORDER:
        return
    case = db.query(Case).filter(Case.id == case_id).first()
    if not case:
        return
    current = case.stage if case.stage in STAGE_ORDER else "oportunidad"
    if STAGE_ORDER.index(stage) > STAGE_ORDER.index(current):
        case.stage = stage


def get_cases(
    db: Session, tenant_id: int, company_id: int, search: str | None = None,
    account_id: int | None = None, stage: str | None = None,
) -> list[Case]:
    q = db.query(Case).filter(
        Case.tenant_id == tenant_id, Case.company_id == company_id, Case.deleted_at.is_(None),
    )
    if account_id is not None:
        q = q.filter(Case.account_id == account_id)
    if stage:
        q = q.filter(Case.stage == stage)
    if search:
        q = q.filter(Case.reference.ilike(f"%{search}%"))
    return q.order_by(Case.created_at.desc()).all()


def get_case(db: Session, case_id: int, tenant_id: int, company_id: int) -> Case:
    obj = (
        db.query(Case)
        .filter(Case.id == case_id, Case.tenant_id == tenant_id, Case.company_id == company_id, Case.deleted_at.is_(None))
        .first()
    )
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Expediente no encontrado")
    return obj


def get_case_by_reference(db: Session, reference: str, tenant_id: int, company_id: int) -> Case:
    obj = (
        db.query(Case)
        .filter(Case.reference == reference, Case.tenant_id == tenant_id, Case.company_id == company_id,
                Case.deleted_at.is_(None))
        .first()
    )
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Expediente no encontrado")
    return obj


def build_timeline(db: Session, case: Case) -> list[dict]:
    """Devuelve la historia del expediente: todas las entidades ligadas por case_id,
    en orden cronológico. Un único lookup para la UI y para otros sistemas."""
    # Import local para evitar ciclos de importación entre módulos.
    from ..opportunities.models import Opportunity
    from ..quotes.models import Quote
    from ..service_requests.models import ServiceRequest
    from api.v1.modules.fin.invoices.models import Invoice
    from api.v1.modules.ops.shipments.models import Shipment

    events: list[dict] = []
    specs = [
        ("oportunidad", Opportunity, "/dashboard/crm/oportunidades"),
        ("solicitud", ServiceRequest, "/dashboard/crm/solicitudes"),
        ("cotizacion", Quote, "/dashboard/crm/cotizaciones"),
        ("operacion", Shipment, "/dashboard/ops/embarques"),
        ("factura", Invoice, "/dashboard/fin/facturas"),
    ]
    for kind, model, base_url in specs:
        rows = db.query(model).filter(model.case_id == case.id, model.deleted_at.is_(None)).all()
        for r in rows:
            events.append({
                "kind": kind,
                "id": r.id,
                "reference": getattr(r, "reference", None),
                "status": getattr(r, "status", None),
                "created_at": r.created_at,
                "url": f"{base_url}/{r.id}",
            })
    events.sort(key=lambda e: e["created_at"])
    return events
