import json
import datetime 
from Consultas_SQL.conexion import get_connectionERP,get_connection
from dotenv import load_dotenv
from typing import Optional, Union

# Cargar variables de entorno desde un archivo .env
load_dotenv()



class PartDTO:
    """
    Data Transfer Object para la información de las partes obtenidas del ERP.
    """

    def __init__(
        self,
        Origin: str,
        PartNum: str,
        PartDescription: Optional[str],
        IsServices: bool,
        LastCost: Optional[float],
        CostDate: Optional[Union[datetime.date, datetime.datetime]],
        LastPrice: Optional[float],
        PriceDate: Optional[Union[datetime.date, datetime.datetime]]
    ):
        self.Origin = Origin
        self.PartNum = PartNum
        self.PartDescription = PartDescription or ""
        self.IsServices = bool(IsServices)
        self.LastCost = round(float(LastCost), 2) if LastCost is not None else None
        self.CostDate = CostDate
        self.LastPrice = round(float(LastPrice), 2) if LastPrice is not None else None
        self.PriceDate = PriceDate

    def to_dict(self) -> dict:
        """Convierte el objeto en un diccionario serializable."""
        return {
            "Origin": self.Origin,
            "PartNum": self.PartNum,
            "PartDescription": self.PartDescription,
            "IsServices": self.IsServices,
            "LastCost": self.LastCost,
            "CostDate": (
                self.CostDate.isoformat() if isinstance(self.CostDate, (datetime.date, datetime.datetime)) else None
            ),
            "LastPrice": self.LastPrice,
            "PriceDate": (
                self.PriceDate.isoformat() if isinstance(self.PriceDate, (datetime.date, datetime.datetime)) else None
            ),
        }

    def __repr__(self) -> str:
        """Representación en string del objeto, útil para debugging."""
        return (
            f"PartDTO(Origin='{self.Origin}', PartNum='{self.PartNum}', "
            f"PartDescription='{self.PartDescription}', IsServices={self.IsServices}, "
            f"LastCost={self.LastCost}, CostDate={self.CostDate}, "
            f"LastPrice={self.LastPrice}, PriceDate={self.PriceDate})"
        )


class Quotation_Service:
    """
    Clase de servicio para manejar la lógica de negocio relacionada con las partes (parts).
    """
    @staticmethod
    def getParts_From_Epicor() :
        """
        Obtiene las partes desde la base de datos de Epicor que empiezan con el string de búsqueda.
        La fecha de la consulta se actualiza dinámicamente al primer día del año en curso.
        """
        
        # 1. Obtener el primer día del año actual en el formato YYYYMMDD
        first_day_of_year = datetime.date.today().replace(month=1, day=1)
        date_str = first_day_of_year.strftime('%Y%m%d')
        print(f"Primer día del año actual: {date_str}")
        
        # 2. Construir la consulta:
        # - La fecha se inserta con f-string (es seguro porque la generamos nosotros).
        # - El filtro LIKE usa un marcador de parámetro '?' para seguridad.
        query = f"""
                    --DECLARE @FechaDesde DATE = '20250101'; -- Ejemplo de fecha fija para pruebas
            
                    WITH UltimaTransaccion AS (
                        SELECT 
                            Erp.PartTran.Company,
                            Erp.PartTran.PartNum,
                            (Erp.PartTran.MtlUnitCost 
                            + Erp.PartTran.LbrUnitCost 
                            + Erp.PartTran.BurUnitCost 
                            + Erp.PartTran.SubUnitCost 
                            + Erp.PartTran.MtlBurdenUnitCost) AS Costo,
                            Erp.PartTran.TranDate,
                            ROW_NUMBER() OVER (
                                PARTITION BY Erp.PartTran.Company, Erp.PartTran.PartNum 
                                ORDER BY Erp.PartTran.TranDate DESC
                            ) AS rn
                        FROM Erp.PartTran
                        WHERE Erp.PartTran.TranType LIKE '%STk'
                        -- AND Erp.PartTran.TranDate >= @FechaDesde -- Solo se prueba cuando esta en sql nativo
                        AND Erp.PartTran.TranDate >= '{date_str}' -- Fecha dinámica
                    )
                    SELECT 
                        'Epicor' AS Origin,
                        Erp.Part.PartNum,
                        Erp.Part.PartDescription,
                        Erp.Part.IsServices,
                        UltimaTransaccion.Costo AS LastCost,
                        UltimaTransaccion.TranDate AS CostDate,
                        NULL AS LastPrice,
                        NULL AS PriceDate
                    FROM Erp.Part
                    LEFT JOIN UltimaTransaccion 
                        ON Erp.Part.Company = UltimaTransaccion.Company
                        AND Erp.Part.PartNum = UltimaTransaccion.PartNum
                        AND UltimaTransaccion.rn = 1
                    WHERE Erp.Part.Company = 'IGSA'
                    AND Erp.Part.InActive = 0
        """

        try:
            with get_connectionERP() as conn:
                with conn.cursor() as cursor:
                    
                    cursor.execute(query)
                    rows = cursor.fetchall()
                    
                    parts_list = []
                    for row in rows:


                        part_obj = PartDTO(
                            Origin=row[0],
                            PartNum=row[1],
                            PartDescription=row[2],
                            IsServices=bool(row[3]),
                            LastCost=row[4],
                            CostDate=row[5],
                            LastPrice=row[6],
                            PriceDate=row[7]
                        )
                        parts_list.append(part_obj)
                    
                    return parts_list
                
        except Exception as e:
            print(f"Error en getParts_From_Epicor: {e}")
            return False


    @staticmethod 
    def getPartes_From_Elephant() :
        """
        Método placeholder para obtener partes desde la base de datos Elephant.
        Actualmente no implementado.
        """
        
        query = f"""
        SELECT 
            'Elephant' AS Origin,      -- valor fijo con alias
            PartNum,
            PartDescription,
            IsServices,
            LastPrice,
            DateQuote,
            NULL AS UltimoCosto,       -- columna fija en NULL
            NULL AS UltimaFecha,       -- otra columna fija en NULL
            Unit
        FROM 
            Part_Quotes;
        """

        try:
            with get_connection() as conn:
                with conn.cursor() as cursor:
                    
                    cursor.execute(query)
                    rows = cursor.fetchall()
                    
                    parts_list = []
                    for row in rows:


                        part_obj = PartDTO(
                            Origin=row[0],
                            PartNum=row[1],
                            PartDescription=row[2],
                            IsServices=bool(row[3]),
                            LastCost=row[4],
                            CostDate=row[5],
                            LastPrice=row[6],
                            PriceDate=row[7]
                        )
                        parts_list.append(part_obj)
                    
                    return parts_list

        except Exception as e:
            print(f"Error en getParts_From_Epicor: {e}")
            return False


    @staticmethod
    def getParts_From_EpicorBy_NoParte(matching_string: str):
        """
        Obtiene las partes desde la base de datos de Epicor que empiezan con el string de búsqueda.
        La fecha de la consulta se actualiza dinámicamente al primer día del año en curso.
        """
        
        # 1. Obtener el primer día del año actual en el formato YYYYMMDD
        first_day_of_year = datetime.date.today().replace(month=1, day=1)
        date_str = first_day_of_year.strftime('%Y%m%d')
        print(f"Primer día del año actual: {date_str}")
        
        # 2. Construir la consulta:
        # - La fecha se inserta con f-string (es seguro porque la generamos nosotros).
        # - El filtro LIKE usa un marcador de parámetro '?' para seguridad.
        query = f"""
        WITH PartsWithCost AS (
            SELECT  top 5
                erp.Part.PartNum,
                erp.Part.PartDescription,
                erp.Part.IsServices,
                (
                    select top 1 (Erp.PartTran.MtlUnitCost + Erp.PartTran.LbrUnitCost + Erp.PartTran.BurUnitCost + Erp.PartTran.SubUnitCost + Erp.PartTran.MtlBurdenUnitCost) as suma
                    from Erp.PartTran
                    where Erp.PartTran.company = erp.Part.Company
                    and Erp.PartTran.PartNum = erp.Part.PartNum
                    and Erp.PartTran.TranType like '%STk'
                    and Erp.PartTran.TranDate >= '{date_str}' -- Fecha dinámica
                    order by suma Desc
                ) as ultimoCosto,
                (
                    select top 1 Erp.PartTran.TranDate
                    from Erp.PartTran
                    where Erp.PartTran.company = erp.Part.Company
                    and Erp.PartTran.PartNum = erp.Part.PartNum
                    and Erp.PartTran.TranType like '%STk'
                    and Erp.PartTran.TranDate >= '{date_str}' -- Fecha dinámica
                    order by (Erp.PartTran.MtlUnitCost + Erp.PartTran.LbrUnitCost + Erp.PartTran.BurUnitCost + Erp.PartTran.SubUnitCost + Erp.PartTran.MtlBurdenUnitCost) Desc
                ) as ultimaFecha
            from 
                erp.Part
            where 
                erp.Part.Company = 'IGSA'
                and erp.Part.InActive = 0
                and erp.Part.PartNum LIKE ? -- Parámetro de búsqueda
        )
        SELECT 
            * 
        FROM 
            PartsWithCost
        ORDER BY 
            ultimoCosto Desc
        """
        
        try:
            with get_connectionERP() as conn:
                with conn.cursor() as cursor:
                    # 3. Preparar el patrón de búsqueda para que coincida con el inicio del string.
                    search_pattern = f"{matching_string}%"
                    
                    # 4. Ejecutar la consulta pasando el patrón como un parámetro seguro.
                    cursor.execute(query, search_pattern)
                    rows = cursor.fetchall()
                    
                    parts_list = []
                    for row in rows:
                        # 5. Mapear a DTO. Los índices se ajustan a ESTA consulta específica.
                        # PartNum -> 0, PartDescription -> 1, IsServices -> 2, etc.
                        part_obj = PartDTO(
                            part_num=row[0],
                            description=row[1],
                            is_services=row[2],
                            last_cost=row[3],
                            last_date=row[4]
                        )
                        parts_list.append(part_obj)
                    
                    return parts_list
                
        except Exception as e:
            print(f"Error en getParts_From_Epicor: {e}")
            return False
        

    