
from typing import Dict, Any, List, Optional
from sqlalchemy.orm import Session

# Import Reference Data Models
from api.v1.modules.public.reference_data.invoice_types.models import InvoiceType
from api.v1.modules.public.reference_data.currency_types.models import CurrencyType
from api.v1.modules.public.reference_data.transport_types.models import TransportType
from api.v1.modules.public.reference_data.customs_sections.models import CustomsSection
from api.v1.modules.public.reference_data.pedimento_codes.models import PedimentoCode
from api.v1.modules.public.reference_data.code_pedimento_regimens.models import CodePedimentoRegimen
from api.v1.modules.public.reference_data.incoterms.models import Incoterm
from api.v1.modules.public.reference_data.valuation_methods.models import ValuationMethod
from api.v1.modules.public.reference_data.transport_modes.models import TransportMode

# Import A76 Services
from api.v1.modules.a76.customs_brokers.services import CustomsBrokerService
from api.v1.modules.a76.clients_and_providers.service import ClientProviderService
from api.v1.modules.a76.transportation.transporters.services import TransporterService
from api.v1.modules.a76.transportation.vehicles.services import VehicleService
from api.v1.modules.a76.transportation.drivers.services import DriverService
from api.v1.modules.a76.transportation.trailers.services import TrailerService
from api.v1.modules.a76.general_catalogs.seal.services import SealService
from api.v1.modules.a76.pedmientos.services.pedimentos import PedimentosService

# Import DTOs for mapping
from api.v1.modules.public.reference_data.invoice_types.dto import InvoiceTypeDTO
from api.v1.modules.a76.customs_brokers.dto import CustomsBrokerResponseDTO
from api.v1.modules.a76.clients_and_providers.dto import ClientProviderResponseDTO
from api.v1.modules.public.reference_data.currency_types.dto import CurrencyTypeDTO
from api.v1.modules.public.reference_data.transport_types.dto import TransportTypeDTO
from api.v1.modules.public.reference_data.customs_sections.dto import CustomsSectionDTO
from api.v1.modules.public.reference_data.code_pedimento_regimens.dto import CodePedimentoRegimenDTO
from api.v1.modules.public.reference_data.incoterms.dto import IncotermDTO
from api.v1.modules.public.reference_data.valuation_methods.dto import ValuationMethodDTO
from api.v1.modules.public.reference_data.transport_modes.dto import TransportModeDTO

# Additional DTOs
from api.v1.modules.a76.transportation.transporters.dto import TransporterResponseDTO
from api.v1.modules.a76.transportation.vehicles.dto import VehicleResponseDTO
from api.v1.modules.a76.transportation.drivers.dto import DriverResponseDTO
from api.v1.modules.a76.transportation.trailers.dto import TrailerResponseDTO
from api.v1.modules.a76.general_catalogs.seal.dto import SealResponseDTO
from api.v1.modules.a76.pedmientos.dtos.pedimentos import PedimentosResponse

from .schemas import InvoiceCatalogsResponse, InvoiceCreationResponse, InvoiceEditionResponse

class InvoiceCatalogService:
    """Service to fetch consolidated catalogs for Invoice views"""

    @staticmethod
    def get_catalogs(db: Session, tenant_id: int, company_id: int) -> InvoiceCatalogsResponse:
        """Fetch all catalogs"""
        
        response = InvoiceCatalogsResponse()

        # Helper to fetch reference data (no company_id needed)
        def fetch_ref_data():
            response.invoice_types = [
                InvoiceTypeDTO.model_validate(obj) for obj in db.query(InvoiceType).all()
            ]
            response.currency_types = [
                 CurrencyTypeDTO.model_validate(obj) for obj in db.query(CurrencyType).all()
            ]
            response.transport_types = [
                TransportTypeDTO.model_validate(obj) for obj in db.query(TransportType).all()
            ]
            response.customs_sections = [
                CustomsSectionDTO.model_validate(obj) for obj in db.query(CustomsSection).all()
            ]
            response.code_pedimento_regimens = [
                CodePedimentoRegimenDTO.model_validate(obj) for obj in db.query(CodePedimentoRegimen).all()
            ]
            response.incoterms = [
                IncotermDTO.model_validate(obj) for obj in db.query(Incoterm).all()
            ]
            response.valuation_methods = [
                ValuationMethodDTO.model_validate(obj) for obj in db.query(ValuationMethod).all()
            ]
            response.transport_modes = [
                TransportModeDTO.model_validate(obj) for obj in db.query(TransportMode).all()
            ]

        # Helper to fetch tenant/company specific data
        def fetch_tenant_data():
            # Customs Brokers
            try:
                brokers, _ = CustomsBrokerService.get_all(db, tenant_id, company_id, limit=1000)
                response.customs_brokers = [
                    CustomsBrokerResponseDTO.model_validate(obj) for obj in brokers
                ]
            except Exception as e:
                print(f"Error fetching customs brokers: {e}")

            # Clients and Providers
            try:
                # Fetch all clients/providers
                # Note: get_all returns list[ClientProvider]
                all_cps, _ = ClientProviderService.get_all(
                    db, tenant_id, company_id, limit=2000
                )
                
                # Helper to safely check client type (handles string or Enum)
                def is_type(obj, types):
                    val = obj.client_or_provider
                    # If it's an enum, get its value, otherwise use as string
                    val_str = val.value if hasattr(val, 'value') else str(val)
                    return val_str in types

                response.clients = [
                    ClientProviderResponseDTO.model_validate(obj) for obj in all_cps 
                    if is_type(obj, ['client', 'both'])
                ]
                response.providers = [
                    ClientProviderResponseDTO.model_validate(obj) for obj in all_cps 
                    if is_type(obj, ['provider', 'both'])
                ]
            except Exception as e:
                print(f"Error fetching clients/providers: {e}")



            # Transporters
            try:
                transporters, _ = TransporterService.get_all(db, tenant_id, company_id, limit=1000)
                response.transporters = [
                    TransporterResponseDTO.model_validate(t) for t in transporters
                ]
            except Exception as e:
                print(f"Error fetching transporters: {e}")

            # Vehicles
            try:
                vehicles, _ = VehicleService.get_all(db, tenant_id, company_id, limit=1000)
                response.vehicles = [
                    VehicleResponseDTO.model_validate(v) for v in vehicles
                ]
            except Exception as e:
                print(f"Error fetching vehicles: {e}")

            # Drivers
            try:
                drivers = DriverService.list_drivers(db, str(company_id), str(tenant_id))
                response.drivers = [
                    DriverResponseDTO.model_validate(d) for d in drivers
                ]
            except Exception as e:
                print(f"Error fetching drivers: {e}")
                # Initialize drivers as empty list if an error occurs
                drivers = []

            # Trailers
            try:
                trailers, _ = TrailerService.get_all(db, tenant_id, company_id, limit=1000)
                response.trailers = [
                    TrailerResponseDTO.model_validate(t) for t in trailers
                ]
            except Exception as e:
                print(f"Error fetching trailers: {e}")

            # Seals
            try:
                seals, _ = SealService.get_all(db, tenant_id, company_id, limit=1000)
                response.seals = [
                    SealResponseDTO.model_validate(s) for s in seals
                ]
            except Exception as e:
                print(f"Error fetching seals: {e}")

            # Pedimentos
            try:
                # Fetch recent pedimentos (e.g. last 100) or filtered if necessary
                pedimentos, _ = PedimentosService.get_all(db, tenant_id, company_id, limit=100)
                response.pedimentos = [
                    # Use dict for now if PedimentosResponse fails due to complexity or just map fields manually if needed
                    # But PedimentosResponse has from_attributes=True
                    # Note: PedimentosResponse structure is complex with nested relations.
                    # If Pedimentos model is fully loaded (eager load in service), this should work.
                    # However, to be safe against recursion or huge payload, we might want a lighter DTO.
                    # Re-using PedimentosResponse for now but be cautious of payload size.
                    PedimentosResponse.model_validate(p) for p in pedimentos
                ]
            except Exception as e:
                print(f"Error fetching pedimentos: {e}")

        try:
            fetch_ref_data()
            fetch_tenant_data()
        except Exception as e:
            print(f"Error fetching catalogs: {e}")
            # In production, we might want to log this properly and potentially return partial data
            # For now, re-raising might be safer to debug, but for resiliency we could suppress.
            # Let's log and re-raise to ensure frontend knows something went wrong during dev.
            import traceback
            traceback.print_exc()
            raise e
        
        return response

    @staticmethod
    def get_creation_data(db: Session, tenant_id: int, company_id: int) -> InvoiceCreationResponse:
        catalogs = InvoiceCatalogService.get_catalogs(db, tenant_id, company_id)
        return InvoiceCreationResponse(
            **catalogs.model_dump(),
            is_create=True,
            filters={}
        )

    @staticmethod
    def get_edition_data(db: Session, invoice_id: int, tenant_id: int, company_id: int) -> Optional[InvoiceEditionResponse]:
        catalogs = InvoiceCatalogService.get_catalogs(db, tenant_id, company_id)
        
        from .services import InvoiceService
        invoice = InvoiceService.get_by_id(db, invoice_id, tenant_id, company_id)
        
        if not invoice:
            return None
            
        return InvoiceEditionResponse(
            **catalogs.model_dump(),
            is_create=False,
            invoice=invoice,
            invoice_id=invoice_id,
            filters={}
        )
