import logging
from typing import Dict, Any, Optional

from core.config import settings
from core.database import get_core_db
from core.exceptions import BaseAPIException
from core.security import collect_user_role_names, get_current_user, validate_access_to_resource
from fastapi import APIRouter, Depends, HTTPException, Query, Path
from sqlalchemy import func, or_, and_
from sqlalchemy.orm import Session

from . import schemas, services, models
from .catalog_service import InvoiceCatalogService
from .permission_map import (
    build_allowed_types_from_view_permissions,
    get_invoice_permission_base,
)

# Create main router
router = APIRouter()
logger = logging.getLogger(__name__)


# --- RUTAS DE UTILIDAD ---

@router.get("/invoices/creation-data", response_model=schemas.InvoiceCreationResponse)
def get_creation_data(
    company_id: int = Query(..., description="Company ID"),
    operation_type: schemas.OperationType = Query(..., description="Operation type for the new invoice"),
    invoice_type: Optional[str] = Query(
        None,
        description="Invoice type key (required for import; optional for export — uses invoice.exp.create)",
    ),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """Get consolidated data for creating a new invoice"""
    try:
        tenant_id = validate_access_to_resource(db, company_id, current_user)
        ot = operation_type.value if hasattr(operation_type, "value") else operation_type
        if ot == "imp":
            if not invoice_type:
                raise HTTPException(
                    status_code=422,
                    detail="invoice_type is required for import invoices",
                )
            perm_base = get_invoice_permission_base(operation_type, invoice_type)
            validate_access_to_resource(
                db,
                company_id,
                current_user,
                required_permissions=[f"{perm_base}.create"],
            )
        else:
            if invoice_type:
                perm_base = get_invoice_permission_base(operation_type, invoice_type)
                validate_access_to_resource(
                    db,
                    company_id,
                    current_user,
                    required_permissions=[f"{perm_base}.create"],
                )
            else:
                validate_access_to_resource(
                    db,
                    company_id,
                    current_user,
                    required_permissions=["invoice.exp.create"],
                )
        return InvoiceCatalogService.get_creation_data(db, tenant_id, company_id)
    except HTTPException:
        raise
    except BaseAPIException:
        raise
    except Exception as e:
        logger.exception("get_creation_data failed: %s", e)
        raise HTTPException(status_code=500, detail=f"Error al cargar datos de creación: {str(e)}")

@router.get("/invoices/{invoice_id}/edition-data", response_model=schemas.InvoiceEditionResponse)
def get_edition_data(
    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),
):
    """Get consolidated data for editing an existing invoice"""
    try:
        tenant_id = validate_access_to_resource(db, company_id, current_user)
        
        # 1. Traer la factura para saber su tipo
        invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
        if not invoice:
            raise HTTPException(status_code=404, detail="Invoice not found")
            
        # 2. Validar permiso dinámico de Edición
        perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
        validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])

        data = InvoiceCatalogService.get_edition_data(db, invoice_id, tenant_id, company_id)
        return data
    except HTTPException:
        raise
    except BaseAPIException:
        raise
    except Exception as e:
        logger.exception("get_edition_data failed: %s", e)
        raise HTTPException(status_code=500, detail=f"Error al cargar datos de edición: {str(e)}")

@router.get("/invoices/remesa-suggestion", response_model=Dict[str, int])
def get_remesa_suggestion(
    pedimento_id: int = Query(..., description="Pedimento ID"),
    company_id: int = Query(..., description="Company ID"),
    operation_type: Optional[schemas.OperationType] = Query(
        None,
        description="Required when no invoice exists yet for this pedimento (context for permission)",
    ),
    invoice_type: Optional[str] = Query(
        None,
        description="Required when no invoice exists yet for this pedimento (context for permission)",
    ),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Sugiere siguiente remesa. Si ya hay facturas ligadas al pedimento, el permiso se infiere de ellas.
    Si no hay facturas en compliance para ese pedimento, pasar operation_type e invoice_type del alta en curso.
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user)

    invoice_for_perm = (
        db.query(models.InvoiceHeader)
        .join(
            models.InvoiceComplianceMx,
            models.InvoiceComplianceMx.invoice_id == models.InvoiceHeader.id,
        )
        .filter(
            models.InvoiceComplianceMx.pedimento_id == pedimento_id,
            models.InvoiceHeader.tenant_id == tenant_id,
            models.InvoiceHeader.company_id == company_id,
        )
        .order_by(models.InvoiceHeader.id.desc())
        .first()
    )
    if invoice_for_perm:
        perm_base = get_invoice_permission_base(
            invoice_for_perm.operation_type, invoice_for_perm.invoice_type
        )
        validate_access_to_resource(
            db,
            company_id,
            current_user,
            required_permissions=[f"{perm_base}.edit"],
        )
    elif operation_type is not None and invoice_type:
        perm_base = get_invoice_permission_base(operation_type, invoice_type)
        validate_access_to_resource(
            db,
            company_id,
            current_user,
            required_permissions=[f"{perm_base}.edit"],
        )
    else:
        raise HTTPException(
            status_code=422,
            detail="operation_type and invoice_type are required when no invoice is linked to this pedimento yet",
        )

    max_rem = (
        db.query(func.max(models.InvoiceComplianceMx.remesa))
        .filter(
            models.InvoiceComplianceMx.pedimento_id == pedimento_id,
            models.InvoiceComplianceMx.tenant_id == tenant_id,
            models.InvoiceComplianceMx.company_id == company_id,
        )
        .scalar()
    )
    return {"next_remesa": int((max_rem or 0) + 1)}


# --- RUTAS CRUD MANUALES (Sustituyen al TenantCRUDRoutes por seguridad) ---

@router.post("/invoices/", response_model=schemas.InvoiceHeaderResponse)
def create_invoice(
    data: schemas.InvoiceHeaderCreate,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    try:
        tenant_id = validate_access_to_resource(db, company_id, current_user)
        
        # Validamos usando los datos que vienen en el body (payload)
        perm_base = get_invoice_permission_base(data.operation_type, data.invoice_type)
        validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.create"])
        
        return services.InvoiceService.create(db, data, tenant_id, company_id)
    except HTTPException:
        raise
    except BaseAPIException:
        raise
    except Exception as e:
        logger.exception("create_invoice failed: %s", e)
        raise HTTPException(status_code=500, detail=f"Error al guardar factura: {str(e)}")

@router.get("/invoices/{invoice_id}", response_model=schemas.InvoiceHeaderResponse)
def get_invoice(
    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),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice:
        raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.view"])
    
    return invoice

@router.put("/invoices/{invoice_id}", response_model=schemas.InvoiceHeaderResponse)
def update_invoice(
    invoice_id: int,
    data: schemas.InvoiceHeaderUpdate,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice:
        raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])
    
    return services.InvoiceService.update(db, invoice_id, tenant_id, data, company_id)

@router.delete("/invoices/{invoice_id}")
def delete_invoice(
    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),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice:
        raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.delete"])
    
    success = services.InvoiceService.delete(db, invoice_id, tenant_id, company_id)
    return {"success": success}


# --- RUTA DE LISTADO (FILTROS) ---

@router.get("/invoices/", response_model=schemas.InvoiceHeaderListResponse)
def list_invoices(
    company_id: int = Query(..., description="Company ID"),
    page: int = Query(1, ge=1, description="Page number"),
    page_size: int = Query(50, ge=1, le=200, description="Items per page"),
    search: str = Query(None, description="Search by invoice number"),
    status: Optional[Any] = Query(None, description="Filter by status"),
    operation_type: schemas.OperationType = Query(None, description="Filter by operation type"),
    invoice_type: str = Query(None, description="Filter by invoice type"),
    manifest_number: str = Query(None, description="Filter by manifest number"),
    pedimento: str = Query(None, description="Filter by pedimento"),
    invoice_number: str = Query(None, description="Filter by invoice number"),
    project_number: str = Query(None, description="Filter by project number"),
    year: str = Query(None, description="Filter by year"),
    sort_by: Optional[str] = Query(None, description="Column to sort by"),
    sort_order: Optional[str] = Query("asc", pattern="^(asc|desc)$", description="Sort order (asc or desc)"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    List invoices with optional filters and granular permission enforcement.
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    # Roles vienen del Hub (/auth/me), no de realm_access del JWT crudo.
    is_hub_admin = "admin" in collect_user_role_names(current_user)

    from api.v1.modules.core.permissions.service import PermissionService

    user_id = current_user.get("sub") or current_user.get("id")
    perm_service = PermissionService(db)
    perm_codes = perm_service.get_user_permissions(user_id, company_id)

    if settings.ENVIRONMENT == "development":
        user_name = current_user.get("preferred_username") or current_user.get(
            "email", "Desconocido"
        )
        logger.debug(
            "[invoices.list] user=%s id=%s company=%s perms=%s",
            user_name,
            user_id,
            company_id,
            perm_codes,
        )

    allowed_filters = build_allowed_types_from_view_permissions(perm_codes)

    if not allowed_filters:
        if is_hub_admin:
            logger.debug(
                "[invoices.list] hub admin without invoice view perms — unfiltered types fallback"
            )
            allowed_filters = None
        else:
            raise HTTPException(
                status_code=403,
                detail="No tienes permisos para ver facturas en esta empresa",
            )

    try:
        skip = (page - 1) * page_size
        
        # Combinar filtros de búsqueda con filtros granulares de permisos
        filters = {
            "invoice_number": invoice_number,
            "status": status,
            "operation_type": operation_type.value if operation_type else None,
            "invoice_type": invoice_type,
            "manifest_number": manifest_number,
            "pedimento": pedimento,
            "project_number": project_number,
            "year": year,
            "allowed_types": allowed_filters
        }
        
        filters = {k: v for k, v in filters.items() if v is not None}

        items, total = services.InvoiceService.get_all(
            db, tenant_id, company_id, skip=skip, limit=page_size, filters=filters, sort_by=sort_by, sort_order=sort_order
        )
        
        return {
            "items": items,
            "total": total,
            "page": page,
            "page_size": page_size
        }
    except BaseAPIException:
        raise
    except Exception as e:
        logger.exception("list_invoices failed: %s", e)
        raise HTTPException(status_code=500, detail=f"Internal server error in invoices list: {str(e)}")


# --- RUTAS DE LOGÍSTICA ---

@router.get("/invoices/{invoice_id}/logistics", response_model=list[schemas.InvoiceLogisticsResponse])
def get_invoice_logistics(
    invoice_id: int = Path(..., description="Invoice ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.view"])

    return services.InvoiceLogisticsService.get_all_by_invoice(db, invoice_id)

@router.post("/invoices/{invoice_id}/logistics", response_model=schemas.InvoiceLogisticsResponse, status_code=201)
def create_invoice_logistics(
    logistics_data: schemas.InvoiceLogisticsCreate,
    invoice_id: int = Path(..., description="Invoice ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    # Editar los hijos cuenta como editar la factura padre
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])

    return services.InvoiceLogisticsService.create(db, logistics_data, invoice_id, tenant_id, company_id)

@router.delete("/invoices/{invoice_id}/logistics/{logistics_id}", status_code=204)
def delete_invoice_logistics(
    invoice_id: int = Path(..., description="Invoice ID"),
    logistics_id: int = Path(..., description="Logistics ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])

    if not services.InvoiceLogisticsService.delete(db, logistics_id, invoice_id):
        raise HTTPException(status_code=404, detail="Logistics entry not found")
    return None


# --- RUTAS DE DETALLES DE VENTA (PARTIDAS) ---

@router.get("/invoices/{invoice_id}/details", response_model=list[schemas.InvoiceSalesDetailsResponse])
def get_invoice_details(
    invoice_id: int = Path(..., description="Invoice ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.view"])

    return services.InvoiceSalesDetailsService.get_all_by_invoice(db, invoice_id)

@router.post("/invoices/{invoice_id}/details", response_model=schemas.InvoiceSalesDetailsResponse, status_code=201)
def create_invoice_detail(
    detail_data: schemas.InvoiceSalesDetailsCreate,
    invoice_id: int = Path(..., description="Invoice ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])

    return services.InvoiceSalesDetailsService.create(db, detail_data, invoice_id, tenant_id, company_id)

@router.delete("/invoices/{invoice_id}/details/{detail_id}", status_code=204)
def delete_invoice_detail(
    invoice_id: int = Path(..., description="Invoice ID"),
    detail_id: int = Path(..., description="Detail ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])

    if not services.InvoiceSalesDetailsService.delete(db, detail_id, invoice_id):
        raise HTTPException(status_code=404, detail="Sales detail not found")
    return None


# --- RUTAS DE COBRANZA (COLLECTIONS) ---

@router.get("/invoices/{invoice_id}/collections", response_model=list[schemas.InvoiceCollectionsResponse])
def get_invoice_collections(
    invoice_id: int = Path(..., description="Invoice ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.view"])

    return services.InvoiceCollectionsService.get_all_by_invoice(db, invoice_id)

@router.post("/invoices/{invoice_id}/collections", response_model=schemas.InvoiceCollectionsResponse, status_code=201)
def create_invoice_collection(
    collection_data: schemas.InvoiceCollectionsCreate,
    invoice_id: int = Path(..., description="Invoice ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])

    return services.InvoiceCollectionsService.create(db, collection_data, invoice_id, tenant_id, company_id)

@router.delete("/invoices/{invoice_id}/collections/{collection_id}", status_code=204)
def delete_invoice_collection(
    invoice_id: int = Path(..., description="Invoice ID"),
    collection_id: int = Path(..., description="Collection ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    invoice = services.InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
    if not invoice: raise HTTPException(status_code=404, detail="Invoice not found")
        
    perm_base = get_invoice_permission_base(invoice.operation_type, invoice.invoice_type)
    validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.edit"])

    if not services.InvoiceCollectionsService.delete(db, collection_id, invoice_id):
        raise HTTPException(status_code=404, detail="Collection not found")
    return None