from typing import List, Optional, Tuple, Dict, Any
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException
import logging
from decimal import Decimal

from .models import USTariffFraction
from .dto import USTariffFractionCreateDTO, USTariffFractionUpdateDTO
from api.v1.modules.sitar.fracciones_usa.service import FraccionesUSAService

logger = logging.getLogger(__name__)


class USTariffFractionMapper:
    """Maps Sitar FraccionesUSAResponse objects to USTariffFraction domain objects"""

    @staticmethod
    def to_domain(
        item: Any, tenant_id: int, company_id: int
    ) -> USTariffFraction:
        """Convert a FraccionesUSAResponse to a USTariffFraction instance (not persisted)"""
        code = item.FRACCION_CON_PUNTO or item.FRACCION_MOSTRAR or item.FRACCION_SIN_PUNTO or ""
        ad_valorem: Optional[float] = None
        if item.TARIFA1:
            try:
                ad_valorem = float(str(item.TARIFA1).replace("%", "").strip())
            except (ValueError, TypeError):
                ad_valorem = None

        fraction = USTariffFraction()
        fraction.id = item.CONSECUTIVO
        fraction.tenant_id = tenant_id
        fraction.company_id = company_id
        fraction.code = code
        fraction.prefix = item.FRACCION_SIN_PUNTO
        fraction.type_code = str(item.NIVEL) if item.NIVEL is not None else None
        fraction.ad_valorem = ad_valorem
        fraction.fixed_cost = None
        fraction.unit_of_measure = item.UNIDADCANTIDAD
        fraction.description = item.DESCRIPCION
        now = datetime.now()
        fraction.created_at = now
        fraction.updated_at = now
        return fraction


class USTariffFractionService:
    """Service para gestionar fracciones arancelarias americanas"""

    @staticmethod
    async def get_all(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None,
    ) -> Tuple[List[USTariffFraction], int]:
        """
        Obtiene todas las fracciones arancelarias americanas con filtros opcionales.
        Estrategia: Sitar API -> Fallback Local DB
        """
        
        ## 1. Try Sitar API
        #try:
        #    sitar_service = FraccionesUSAService.get_instance()
        #    
        #    sitar_fraccion = None
        #    has_filters = False
        #    
        #    if filters and filters.get("search"):
        #        term = filters["search"]
        #        # Sitar only filters by fraction code
        #        if term.replace(".", "").isdigit():
        #            sitar_fraccion = term
        #            has_filters = True
        #    
        #    sitar_items = await sitar_service.search(
        #        fraccion=sitar_fraccion,
        #        skip=skip,
        #        limit=limit
        #    )
        #    
        #    # If Sitar returns empty list, attempt fallback to local DB
        #    if not sitar_items:
        #         logger.info(f"Sitar returned no results for USA query (filters={has_filters}). Attempting fallback to local DB.")
        #         return USTariffFractionService._get_all_local(db, tenant_id, company_id, skip, limit, filters)
        #
        #    # Map items
        #    items = [USTariffFractionMapper.to_domain(item, tenant_id, company_id) for item in sitar_items]
        #    
        #    # Estimate total
        #    total = len(items) + skip
        #    if len(items) == limit:
        #        total += 1
        #        
        #    return items, total

        #except Exception as e:           
        return USTariffFractionService._get_all_local(db, tenant_id, company_id, skip, limit, filters)

    @staticmethod
    def _get_all_local(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None,
    ) -> Tuple[List[USTariffFraction], int]:
        """Lógica original de consulta local"""
        query = db.query(USTariffFraction).filter(
            USTariffFraction.tenant_id == tenant_id,
            USTariffFraction.company_id == company_id,
        )

        # Aplicar filtros
        if filters:
            # Si hay un filtro 'search', buscar en múltiples campos
            if filters.get("search"):
                search_term = f"%{filters['search']}%"
                query = query.filter(
                    USTariffFraction.code.ilike(search_term) |
                    USTariffFraction.description.ilike(search_term) |
                    USTariffFraction.prefix.ilike(search_term)
                )

        total = query.count()
        items = query.offset(skip).limit(limit).all()

        return items, total        

    @staticmethod
    def get_by_id(
        db: Session, fraction_id: int, tenant_id: int, company_id: int
    ) -> Optional[USTariffFraction]:
        """
        Obtiene por ID local.
        """
        return (
            db.query(USTariffFraction)
            .filter(
                USTariffFraction.id == fraction_id,
                USTariffFraction.tenant_id == tenant_id,
                USTariffFraction.company_id == company_id,
            )
            .first()
        )

    @staticmethod
    def create(
        db: Session,
        fraction_data: USTariffFractionCreateDTO,
        tenant_id: int,
        company_id: int,
    ) -> USTariffFraction:
        try:
            db_fraction = USTariffFraction(
                tenant_id=tenant_id,
                company_id=company_id,
                **fraction_data.model_dump(),
            )
            db.add(db_fraction)
            db.commit()
            db.refresh(db_fraction)
            return db_fraction
        except IntegrityError as e:
            db.rollback()
            logger.error(f"Error creando fracción americana: {e}")
            raise HTTPException(
                status_code=400,
                detail="Ya existe una fracción americana con este código",
            )

    @staticmethod
    def update(
        db: Session,
        fraction_id: int,
        tenant_id: int,
        fraction_data: USTariffFractionUpdateDTO,
        company_id: int,
    ) -> Optional[USTariffFraction]:
        db_fraction = USTariffFractionService.get_by_id(
            db, fraction_id, tenant_id, company_id
        )
        if not db_fraction:
            return None

        update_data = fraction_data.model_dump(exclude_unset=True)
        for field, value in update_data.items():
            setattr(db_fraction, field, value)

        db.commit()
        db.refresh(db_fraction)
        return db_fraction

    @staticmethod
    def delete(
        db: Session, fraction_id: int, tenant_id: int, company_id: int
    ) -> bool:
        db_fraction = USTariffFractionService.get_by_id(
            db, fraction_id, tenant_id, company_id
        )
        if not db_fraction:
            return False

        db.delete(db_fraction)
        db.commit()
        return True
