"""PDF de cotización, configuración de marca por tenant y envío por correo."""

from __future__ import annotations

import logging
from datetime import datetime, timezone

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

from ..accounts.models import Account
from ..service_requests.models import ServiceRequest
from .models import Quote, QuoteItem, QuoteSettings
from .pdf import build_quote_pdf
from .service import get_quote

logger = logging.getLogger(__name__)

DEFAULT_TERMS = (
    "Tarifas sujetas a disponibilidad de espacio.\n"
    "Cualquier variación en peso o volumen generará ajuste tarifario.\n"
    "No incluye cargos extraordinarios, maniobras especiales o servicios no especificados.\n"
    "Tarifas sujetas a revisión por parte de la línea transportista y autoridades correspondientes."
)


# ---------------- Configuración de marca ----------------
def get_settings(db: Session, tenant_id: int, company_id: int) -> QuoteSettings | None:
    return (
        db.query(QuoteSettings)
        .filter(QuoteSettings.tenant_id == tenant_id, QuoteSettings.company_id == company_id,
                QuoteSettings.deleted_at.is_(None))
        .first()
    )


def upsert_settings(db: Session, tenant_id: int, company_id: int, data: dict) -> QuoteSettings:
    obj = get_settings(db, tenant_id, company_id)
    if obj is None:
        obj = QuoteSettings(tenant_id=tenant_id, company_id=company_id)
        db.add(obj)
    for field, value in data.items():
        if value is not None:
            setattr(obj, field, value)
    db.commit()
    db.refresh(obj)
    return obj


def set_logo_key(db: Session, tenant_id: int, company_id: int, file_key: str) -> QuoteSettings:
    obj = get_settings(db, tenant_id, company_id)
    if obj is None:
        obj = QuoteSettings(tenant_id=tenant_id, company_id=company_id)
        db.add(obj)
    obj.logo_file_key = file_key
    db.commit()
    db.refresh(obj)
    return obj


def _compose_place(city: str | None, country: str | None, port: str | None) -> str | None:
    """Arma 'Ciudad, PAÍS (Puerto)' con las partes que existan (ruta estructurada)."""
    head = ", ".join(p for p in (city, country) if p)
    if port:
        head = f"{head} ({port})" if head else port
    return head or None


def _company_row(db: Session, company_id: int) -> dict:
    try:
        row = db.execute(
            text("SELECT name, rfc, logo FROM a76.company WHERE id = :c"), {"c": company_id}
        ).first()
        if row:
            return {"name": row[0], "rfc": row[1], "logo": row[2]}
    except Exception:
        pass
    return {}


# ---------------- Construcción del PDF ----------------
def build_pdf_bytes(db: Session, quote: Quote, tenant_id: int, company_id: int) -> bytes:
    items = (
        db.query(QuoteItem)
        .filter(QuoteItem.quote_id == quote.id, QuoteItem.deleted_at.is_(None))
        .order_by(QuoteItem.id.asc())
        .all()
    )
    account = (
        db.query(Account).filter(Account.id == quote.account_id).first() if quote.account_id else None
    )
    sr = (
        db.query(ServiceRequest).filter(ServiceRequest.id == quote.service_request_id).first()
        if quote.service_request_id else None
    )
    settings = get_settings(db, tenant_id, company_id)
    company = _company_row(db, company_id)

    # Emisor: config del tenant con respaldo en a76.company
    emitter = {
        "name": (settings.emitter_name if settings else None) or company.get("name") or "Emisor",
        "rfc": (settings.emitter_rfc if settings else None) or company.get("rfc"),
        "address": settings.emitter_address if settings else None,
        "phone": settings.emitter_phone if settings else None,
        "email": settings.emitter_email if settings else None,
        "website": settings.emitter_website if settings else None,
    }
    accent = (settings.accent_color if settings else None) or "#12294c"
    prefix = (settings.quote_prefix if settings else None) or "COT"
    terms = quote.terms or (settings.default_terms if settings else None) or DEFAULT_TERMS
    footer = settings.footer_note if settings else None

    # Logo (MinIO)
    logo_bytes = None
    logo_key = settings.logo_file_key if settings else None
    if logo_key:
        try:
            from core.storage_s3 import get_object_bytes
            logo_bytes = get_object_bytes(logo_key)
        except Exception as exc:
            logger.warning("No se pudo leer el logo del tarifario: %s", exc)

    reference = quote.reference or f"{prefix}-{datetime.now().strftime('%Y%m%d')}-{quote.id:03d}"
    head = {
        "reference": reference,
        "issue_date": quote.issue_date.isoformat() if quote.issue_date else None,
        "valid_until": quote.valid_until.isoformat() if quote.valid_until else None,
        "owner": quote.owner_user_id or "-",
        "status": quote.status,
    }
    client = {
        "name": account.name if account else None,
        "rfc": account.rfc if account else None,
        "email": account.email if account else None,
        "phone": account.phone if account else None,
    }
    cargo = []
    route = []
    if sr:
        cargo = [
            ("Tipo de mercancía", sr.cargo_type), ("Descripción", sr.commodity),
            ("Peso", str(sr.weight) if sr.weight is not None else None),
            ("Volumen", str(sr.volume) if sr.volume is not None else None),
            ("Tipo de carga", sr.load_type), ("Equipo", sr.container_equipment),
        ]
        route = [
            ("Operación", sr.operation_type), ("Modo", sr.transport_mode),
            ("Servicio", sr.service_type), ("Incoterm", sr.incoterm),
            ("Origen", sr.origin or _compose_place(sr.origin_city, sr.origin_country, sr.origin_port)),
            ("Destino", sr.destination or _compose_place(sr.destination_city, sr.destination_country, sr.destination_port)),
            ("Fecha requerida", sr.required_date.isoformat() if sr.required_date else None),
        ]

    return build_quote_pdf(
        emitter=emitter, head=head, client=client, cargo=cargo, route=route,
        items=[{"concept": i.concept, "description": i.description, "quantity": i.quantity, "unit_sale": i.unit_sale} for i in items],
        currency=quote.currency, subtotal=quote.total_sale, terms=terms, footer=footer,
        logo_bytes=logo_bytes, accent=accent,
    )


def _store_pdf(db: Session, quote: Quote, tenant_id: int, company_id: int, pdf_bytes: bytes) -> str:
    from core.storage_s3 import put_object_bytes
    ref = (quote.reference or f"cot-{quote.id}").replace("/", "-")
    key = f"tenants/{tenant_id}/companies/{company_id}/crm-quotes/{quote.id}/cotizacion-{ref}.pdf"
    put_object_bytes(key, pdf_bytes, content_type="application/pdf")
    quote.pdf_file_key = key
    db.commit()
    return key


def get_pdf_url(db: Session, quote_id: int, tenant_id: int, company_id: int) -> str:
    from core.storage_s3 import presigned_get_url
    quote = get_quote(db, quote_id, tenant_id, company_id)
    pdf_bytes = build_pdf_bytes(db, quote, tenant_id, company_id)
    key = _store_pdf(db, quote, tenant_id, company_id, pdf_bytes)
    return presigned_get_url(key)


# ---------------- Envío por correo ----------------
async def send_quote_email(
    db: Session, quote_id: int, tenant_id: int, company_id: int,
    to: str | None, subject: str | None, message: str | None,
) -> dict:
    import ssl
    from email import encoders
    from email.mime.base import MIMEBase
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText

    import aiosmtplib

    from core.config import settings as cfg

    quote = get_quote(db, quote_id, tenant_id, company_id)
    account = db.query(Account).filter(Account.id == quote.account_id).first() if quote.account_id else None
    recipient = to or (account.email if account else None)
    if not recipient:
        raise HTTPException(status_code=400, detail="No hay correo destino (captura uno o pon el correo del cliente).")

    pdf_bytes = build_pdf_bytes(db, quote, tenant_id, company_id)
    _store_pdf(db, quote, tenant_id, company_id, pdf_bytes)
    ref = quote.reference or f"COT-{quote.id}"

    msg = MIMEMultipart()
    msg["From"] = f"{cfg.SMTP_FROM_NAME} <{cfg.SMTP_USER}>"
    msg["To"] = recipient
    msg["Subject"] = subject or f"Cotización {ref}"
    html = (
        "<div style='font-family:Arial,sans-serif;color:#333;max-width:600px'>"
        f"<p>{(message or 'Adjunto la cotización solicitada. Quedamos atentos.').replace(chr(10), '<br>')}</p>"
        f"<p style='color:#6b7280;font-size:12px'>Cotización {ref}</p></div>"
    )
    msg.attach(MIMEText(html, "html"))
    part = MIMEBase("application", "pdf")
    part.set_payload(pdf_bytes)
    encoders.encode_base64(part)
    part.add_header("Content-Disposition", f'attachment; filename="cotizacion-{ref}.pdf"')
    msg.attach(part)

    if not (cfg.SMTP_USER and cfg.SMTP_PASSWORD):
        raise HTTPException(status_code=503, detail="El correo saliente (SMTP) no está configurado en el servidor.")
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    try:
        # Puerto 465 = SSL implícito; los demás (587/2525/…) = STARTTLS.
        await aiosmtplib.send(
            msg,
            hostname=cfg.SMTP_HOST,
            port=cfg.SMTP_PORT,
            username=cfg.SMTP_USER,
            password=cfg.SMTP_PASSWORD,
            use_tls=(cfg.SMTP_PORT == 465),
            start_tls=(cfg.SMTP_PORT != 465),
            tls_context=ctx,
            validate_certs=False,
            timeout=30,
        )
    except Exception as exc:
        logger.error("Error enviando cotización %s: %s", quote_id, exc)
        raise HTTPException(status_code=502, detail=f"No se pudo enviar el correo: {exc}")

    # Marca como enviada
    if quote.status == "borrador":
        quote.status = "enviada"
    quote.sent_at = datetime.now(timezone.utc)
    db.commit()
    return {"sent_to": recipient, "reference": ref}
