from __future__ import annotations

import io
import logging
from typing import Any, Dict

from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session

from core import storage_s3
from core.celery_app import celery_app
from core.config import settings
from core.database import get_core_db
from core.exceptions import ValidationException
from core.s3_keys import cove_acuse_pdf_key
from core.security import get_current_user, get_tenant_from_token, validate_access_to_resource

from api.v1.modules.core.tasks_tracking import track_and_dispatch

from .schemas import (
    CoveEligibilityResponse,
    FacturaCoveResponse,
    GenerateCoveFromInvoiceRequest,
)
from .service import FacturaCoveDomainService
from .tasks import factura_cove_generate

logger = logging.getLogger(__name__)

router = APIRouter()


@router.post(
    "/invoices/{invoice_id}/cove",
    response_model=FacturaCoveResponse,
    summary="Generar COVE a partir de una factura (asíncrono)",
)
def trigger_cove_for_invoice(
    invoice_id: int,
    body: GenerateCoveFromInvoiceRequest,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Dispara la tarea Celery `factura_cove_generate` para una factura específica.

    - Valida acceso a la compañía.
    - Registra la tarea en el tracker de tareas.
    - Retorna el `task_id` para hacer polling de estado desde el frontend.
    """
    tenant_id = validate_access_to_resource(db, body.company_id, current_user)

    # Validación mínima de existencia/propiedad de la factura (el dominio hará validaciones más profundas).
    from api.v1.modules.a76.invoices.models import InvoiceHeader

    invoice: InvoiceHeader | None = db.get(InvoiceHeader, invoice_id)
    if not invoice:
        raise HTTPException(status_code=404, detail=f"Factura {invoice_id} no encontrada.")
    if invoice.company_id != body.company_id or invoice.tenant_id != tenant_id:
        raise HTTPException(
            status_code=400,
            detail="La factura no pertenece a la compañía o tenant actuales.",
        )

    task = track_and_dispatch(
        db=db,
        task=factura_cove_generate,
        tenant_id=tenant_id,
        company_id=body.company_id,
        requested_by_user=(
            current_user.get("email")
            or current_user.get("preferred_username")
            or current_user.get("username")
            or "system"
        ),
        task_name="factura_cove_generate",
        task_group="factura_cove",
        task_origin="a76/factura_cove/invoices/cove",
        args=[invoice_id, int(tenant_id), body.company_id, body.recipient_email],
    )

    return FacturaCoveResponse(
        task_id=task.id,
        status="queued",
        message="Tarea de validación/generación de COVE encolada.",
    )


@router.get(
    "/invoices/cove/{task_id}/status",
    summary="Estado de tarea de COVE para factura",
)
def get_cove_status(task_id: str) -> Dict[str, Any]:
    """
    Consulta el estado de una tarea Celery de generación de COVE.

    Retorna:
      - state: 'PROCESSING' | 'SUCCESS' | 'FAILURE'
      - info: { current: int, status: str }  (cuando state == 'PROCESSING')
      - result: dict                          (cuando state == 'SUCCESS' o 'FAILURE')
    """
    task_result = celery_app.AsyncResult(task_id)

    if task_result.state in ("PENDING", "STARTED"):
        return {
            "state": "PROCESSING",
            "info": {"current": 0, "status": "Iniciando generación de COVE..."},
        }

    if task_result.state == "PROGRESS":
        return {
            "state": "PROCESSING",
            "info": task_result.info or {"current": 0, "status": "Procesando COVE..."},
        }

    if task_result.state == "SUCCESS":
        return {
            "state": "SUCCESS",
            "result": task_result.result,
        }

    error_info = task_result.result
    if isinstance(error_info, Exception):
        error_msg = str(error_info)
    else:
        error_msg = str(error_info) if error_info else "Error desconocido"

    return {
        "state": "FAILURE",
        "result": error_msg,
    }


@router.get(
    "/invoices/{invoice_id}/cove/eligibility",
    response_model=CoveEligibilityResponse,
    summary="Verifica si una factura puede generar COVE",
)
def check_cove_eligibility(
    invoice_id: int,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Evalúa si la factura tiene todos los datos necesarios (VU, factura, partidas)
    para poder generar un COVE. No dispara la tarea Celery.
    """
    tenant_id = get_tenant_from_token(current_user)
    if not tenant_id:
        raise HTTPException(status_code=400, detail="Tenant ID not found in token")

    tenant_id_int = int(tenant_id)

    service = FacturaCoveDomainService(db)
    eligibility = service.check_eligibility(invoice_id=invoice_id, tenant_id=tenant_id_int, company_id=company_id)
    return eligibility


@router.get(
    "/invoices/{invoice_id}/cove/acuse",
    summary="Generar o descargar el Acuse de COVE en PDF",
    responses={
        200: {"content": {"application/pdf": {}}, "description": "PDF de Acuse de COVE"},
        422: {"description": "La factura aún no tiene XML de COVE asociado"},
        404: {"description": "Factura no encontrada"},
    },
)
def get_cove_acuse_pdf(
    invoice_id: int,
    company_id: int = Query(..., description="Company ID"),
    force_refresh: bool = Query(False, description="Forzar regeneración del PDF ignorando la caché S3"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Genera el PDF de Acuse de COVE para una factura.

    - Construye el contexto del reporte a partir del XML de COVE almacenado
      en la configuración VU (agente / empresa).
    - Renderiza el PDF con Jinja2 + pdfkit/wkhtmltopdf (mismo patrón que DODA).
    - Cachea el PDF en S3 con clave estable; en la siguiente llamada lo reutiliza.
    - Usa ``force_refresh=true`` para saltar la caché y regenerar el PDF.
    - Devuelve StreamingResponse con Content-Type application/pdf.

    Si aún no existe un XML de COVE asociado (consulta a VU no realizada),
    el servicio devolverá un error 422.
    """
    from api.v1.modules.a76.invoices.models import InvoiceHeader
    from .report_service import CoveAcuseReportService

    tenant_id = int(validate_access_to_resource(db, company_id, current_user))

    invoice: InvoiceHeader | None = db.get(InvoiceHeader, invoice_id)
    if not invoice:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Factura {invoice_id} no encontrada.",
        )
    if invoice.company_id != company_id or invoice.tenant_id != tenant_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="La factura no pertenece a la compañía o tenant actuales.",
        )

    pdf_key = cove_acuse_pdf_key(tenant_id, company_id, invoice_id)
    filename = f"acuse_cove_{invoice.invoice_number or invoice_id}.pdf"

    # ── Caché S3: reutilizar si el PDF ya existe y no se fuerza regeneración ─
    if not force_refresh and settings.use_s3_object_storage and storage_s3.object_exists(pdf_key):
        try:
            cached_pdf = storage_s3.get_object_bytes(pdf_key)
            if cached_pdf:
                return StreamingResponse(
                    io.BytesIO(cached_pdf),
                    media_type="application/pdf",
                    headers={"Content-Disposition": f'inline; filename="{filename}"'},
                )
        except Exception:
            logger.warning("No se pudo leer el PDF cacheado de S3 para factura %s; regenerando.", invoice_id)

    # ── Generar PDF ─────────────────────────────────────────────
    try:
        svc = CoveAcuseReportService()
        pdf_bytes = svc.build_pdf(db, invoice_id, tenant_id, company_id)
    except ValidationException as exc:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail=exc.message,
        )
    except Exception as exc:
        logger.exception("Error generando PDF de Acuse de COVE para factura %s", invoice_id)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error al generar el Acuse de COVE: {exc}",
        )

    # ── Guardar en S3 ───────────────────────────────────────────
    if settings.use_s3_object_storage:
        try:
            storage_s3.put_object_bytes(pdf_key, pdf_bytes, content_type="application/pdf")
        except Exception:
            logger.warning("No se pudo guardar el PDF de Acuse de COVE en S3 para factura %s.", invoice_id)

    return StreamingResponse(
        io.BytesIO(pdf_bytes),
        media_type="application/pdf",
        headers={"Content-Disposition": f'inline; filename="{filename}"'},
    )

