import json
import os
from datetime import datetime
from ..models.rate import Rate
from ..extensions import db

class TariffComparison:
    def __init__(self):
        self.best_rates = None
        self.load_best_rates()
    
    def load_best_rates(self):
        """Carga las mejores tarifas desde la base de datos."""
        try:
            # Obtener todas las tarifas de la base de datos
            rates = Rate.query.all()
            # Convertir objetos Rate a diccionarios para mantener compatibilidad
            rates_list = [rate.to_dict() for rate in rates]
            self.best_rates = {"rates": rates_list}
        except Exception as e:
            print(f"Error al cargar tarifas de la base de datos: {e}")
            self.best_rates = {"rates": []}
    
    def find_matching_rate(self, tariff_type):
        """Encuentra la mejor tarifa para un tipo de tarifa específico."""
        if not self.best_rates:
            return None
        
        today = datetime.now().date()
        today_str = today.strftime("%Y-%m-%d")
        
        for rate in self.best_rates.get("rates", []):
            valid_from = rate.get("valid_from", "2000-01-01")
            valid_until = rate.get("valid_until", "2099-12-31")
            
            # Asegurarse de que tenemos strings para comparar
            if isinstance(valid_from, datetime):
                valid_from = valid_from.strftime("%Y-%m-%d")
            if isinstance(valid_until, datetime):
                valid_until = valid_until.strftime("%Y-%m-%d")
                
            if (rate["tariff_type"] == tariff_type and
                    valid_from <= today_str and
                    valid_until >= today_str):
                return rate
        
        return None
    
    def get_all_rates(self, tariff_type=None):
        """Obtiene todas las tarifas disponibles, opcionalmente filtradas por tipo."""
        if not self.best_rates:
            return []
        
        rates = self.best_rates.get("rates", [])
        
        # Si se especifica un tipo de tarifa, filtrar por ese tipo
        if tariff_type:
            today = datetime.now().date()
            today_str = today.strftime("%Y-%m-%d")
            
            filtered_rates = []
            for rate in rates:
                if rate["tariff_type"] == tariff_type:
                    # Verificar vigencia
                    valid_from = rate.get("valid_from", "2000-01-01")
                    valid_until = rate.get("valid_until", "2099-12-31")
                    
                    # Asegurarse de que tenemos strings para comparar
                    if isinstance(valid_from, datetime):
                        valid_from = valid_from.strftime("%Y-%m-%d")
                    if isinstance(valid_until, datetime):
                        valid_until = valid_until.strftime("%Y-%m-%d")
                    
                    if valid_from <= today_str and valid_until >= today_str:
                        filtered_rates.append(rate)
            
            return filtered_rates
        
        return rates
    
    def calculate_savings(self, client_data, matching_rate):
        """
        Calcula el ahorro potencial comparando la tarifa actual del cliente
        con nuestra mejor tarifa.
        
        client_data debe contener:
        - current_energy_prices: dict con precios por periodo (P1, P2, P3...)
        - current_power_prices: dict con precios por periodo
        - consumption: dict con consumo en kWh por periodo
        - power: dict con potencia contratada en kW por periodo
        - billing_period_days: número de días del periodo de facturación
        - discounts: valor total de los descuentos aplicados (opcional)
        """
        if not matching_rate:
            return None
        
        # Verificar que tenemos datos suficientes para hacer el cálculo
        if not client_data.get("consumption") or not client_data.get("power"):
            print("ERROR: Faltan datos de consumo o potencia contratada")
            return None

        if not client_data.get("current_energy_prices") or not client_data.get("current_power_prices"):
            print("ERROR: Faltan datos de precios actuales de energía o potencia")
            return None
            
        # Factor para normalizar el consumo a 30 días (mes estándar)
        billing_period_days = client_data.get("billing_period_days", 30)
        monthly_factor = 30 / billing_period_days if billing_period_days > 0 else 1
        
        print(f"DEBUG CLIENT DATA: {json.dumps(client_data, indent=2, default=str)}")
        print(f"DEBUG MATCHING RATE: {json.dumps(matching_rate, indent=2, default=str)}")
        
        # Cálculo del coste energético actual para el periodo de facturación
        # Consumo ya está en kWh para todo el periodo, lo multiplicamos por el precio por kWh
        current_energy_cost = 0
        
        # Verificar si solo hay un precio para P1 (precio único para todos los periodos)
        price_p1 = client_data["current_energy_prices"].get("P1", 0) or 0
        has_other_prices = False
        
        for p in ["P2", "P3", "P4", "P5", "P6"]:
            if client_data["current_energy_prices"].get(p):
                has_other_prices = True
                break
        
        # Si solo hay precio para P1, aplicar ese precio a todos los periodos
        if price_p1 > 0 and not has_other_prices:
            print(f"DEBUG: Detectado precio único de energía: {price_p1} €/kWh")
            total_consumption = 0
            for period in client_data["consumption"].keys():
                consumption = client_data["consumption"].get(period, 0) or 0
                if consumption is not None and consumption > 0:
                    total_consumption += consumption
            
            current_energy_cost = total_consumption * price_p1
            print(f"DEBUG: Consumo total: {total_consumption} kWh * {price_p1} €/kWh = {current_energy_cost} €")
        else:
            # Cálculo normal periodo por periodo
            for period in client_data["consumption"].keys():
                consumption = client_data["consumption"].get(period, 0) or 0
                price = client_data["current_energy_prices"].get(period) or 0
                if consumption is not None and price is not None:
                    current_energy_cost += consumption * price
        
        # Cálculo del coste de energía reactiva actual para el periodo de facturación
        # Primero verificamos si tenemos el coste total de reactiva extraído directamente de la factura
        if client_data.get("reactive_energy_total_cost") is not None and client_data.get("reactive_energy_total_cost") > 0:
            # Usamos el valor real extraído de la factura
            raw_reactive_cost = client_data.get("reactive_energy_total_cost")
            print(f"DEBUG: Usando coste total de reactiva extraído directamente: {raw_reactive_cost} €")
            
            # Normalizar el coste de energía reactiva a un mes estándar de 30 días
            current_reactive_cost = raw_reactive_cost * monthly_factor
            print(f"DEBUG: Coste reactivo normalizado (factor {monthly_factor}): {current_reactive_cost} €")
        else:
            # Fallback: Cálculo tradicional por períodos si no tenemos el dato extraído
            current_reactive_cost = 0
            if client_data.get("reactive_energy"):
                print(f"DEBUG REACTIVE ENERGY: {client_data.get('reactive_energy')}")
                print(f"DEBUG REACTIVE ENERGY PRICES: {client_data.get('reactive_energy_prices')}")
                print(f"DEBUG: Desglose de costes por períodos de energía reactiva:")
                for period in client_data.get("reactive_energy", {}).keys():
                    reactive = client_data.get("reactive_energy", {}).get(period, 0) or 0
                    reactive_price = client_data.get("reactive_energy_prices", {}).get(period) or 0
                    if reactive is not None and reactive_price is not None and reactive > 0 and reactive_price > 0:
                        period_cost = reactive * reactive_price
                        current_reactive_cost += period_cost
                        print(f"DEBUG: Período {period}: {reactive} kVArh * {reactive_price} €/kVArh = {period_cost} €")
                
                print(f"DEBUG: Coste total reactiva sin normalizar: {current_reactive_cost} €")
            
            # Normalizar el coste de energía reactiva a un mes estándar de 30 días
            # Aplicación del factor mensual al coste reactivo
            raw_reactive_cost = current_reactive_cost
            current_reactive_cost = current_reactive_cost * monthly_factor
            print(f"DEBUG: Coste reactivo sin normalizar: {raw_reactive_cost} €")
            print(f"DEBUG: Coste reactivo normalizado (factor {monthly_factor}): {current_reactive_cost} €")
            
        # Mostramos siempre los datos detallados de reactiva si están disponibles (para información)
        if client_data.get("reactive_energy"):
            print("\nDETALLES DE ENERGÍA REACTIVA (solo informativo):")
            for period in client_data.get("reactive_energy", {}).keys():
                reactive = client_data.get("reactive_energy", {}).get(period, 0) or 0
                reactive_price = client_data.get("reactive_energy_prices", {}).get(period) or 0
                if reactive is not None and reactive_price is not None and reactive > 0 and reactive_price > 0:
                    period_cost = reactive * reactive_price
                    print(f"  - Período {period}: {reactive} kVArh * {reactive_price} €/kVArh = {period_cost} €")
        
        # --- Cálculo del coste de potencia actual (mensual normalizado 30 días) ---
        def detect_power_price_unit(price):
            # Detectar automáticamente la unidad basada en el valor del precio
            if price > 10:  # Valores altos típicos de precios anuales (€/kW/año)
                return 'year'
            elif price > 0.5:  # Valores medios típicos de precios mensuales (€/kW/mes)
                return 'month'
            else:  # Valores bajos típicos de precios diarios (€/kW/día)
                return 'day'
        
        def price_day(price, unit):
            # Si no se especifica unidad, detectarla automáticamente
            if not unit or unit == 'auto':
                unit = detect_power_price_unit(price)
                print(f"DEBUG: Unidad de potencia detectada automáticamente: {unit} para precio {price}")
            
            # Convertir a precio diario según la unidad
            if unit == 'day':
                return price
            if unit == 'month':
                return price / 30
            if unit == 'year':
                return price / 365
            
            # Fallback si la unidad no se reconoce, usar detección automática
            detected_unit = detect_power_price_unit(price)
            print(f"DEBUG: Unidad no reconocida, detectando automáticamente: {detected_unit} para precio {price}")
            return price_day(price, detected_unit)
        
        power_units_current = client_data.get('current_power_price_units', {})
        current_power_cost = 0
        
        print(f"DEBUG: Desglose detallado del cálculo de potencia:")
        for period in client_data["power"].keys():
            power = client_data["power"].get(period, 0) or 0
            price = client_data["current_power_prices"].get(period) or 0
            
            if power is not None and price is not None and power > 0 and price > 0:
                # Detectar unidad si no está especificada
                unit = power_units_current.get(period, 'auto')
                
                # Convertir a precio diario
                daily_price = price_day(price, unit)
                
                # Calcular coste mensual (30 días)
                period_cost = power * daily_price * 30
                current_power_cost += period_cost
                
                print(f"DEBUG: Periodo {period}: Potencia {power} kW * Precio {price} €/kW/{unit} = Precio diario {daily_price:.6f} €/kW/día * 30 días = {period_cost:.2f} €/mes")
        
        # Normalizar el coste de energía a un mes estándar de 30 días
        # Esto permite comparaciones justas independientemente de la duración del periodo de facturación
        current_energy_cost = current_energy_cost * monthly_factor
        
        # Procesar los descuentos si existen
        current_discounts = 0
        if client_data.get("discounts") is not None:
            try:
                current_discounts = float(client_data.get("discounts", 0))
                # Normalizar los descuentos al mismo periodo mensual estándar
                current_discounts = current_discounts * monthly_factor
                print(f"DEBUG: Descuentos detectados: {current_discounts} €/mes (normalizado)")
            except (ValueError, TypeError):
                print(f"ERROR: No se pudo convertir el valor de descuentos: {client_data.get('discounts')}")
        
        # Cálculo del coste total actual (mensualizado y normalizado)
        # Asegurar que todas las variables son numéricas antes de la suma
        current_energy_cost = current_energy_cost or 0
        current_power_cost = current_power_cost or 0
        current_reactive_cost = current_reactive_cost or 0
        current_discounts = current_discounts or 0
        
        # Ahora restamos los descuentos del coste total
        current_total_cost = current_energy_cost + current_power_cost + current_reactive_cost - current_discounts
        
        # Cálculo del coste energético con la mejor tarifa para el periodo de facturación
        new_energy_cost = 0
        for period in client_data["consumption"].keys():
            consumption = client_data["consumption"].get(period, 0) or 0
            price = matching_rate["energy_prices"].get(period) or 0
            if consumption is not None and price is not None:
                new_energy_cost += consumption * price
        
        # Cálculo del coste de potencia con la mejor tarifa (mensual normalizado 30 días)
        new_power_cost = 0
        print(f"\nDEBUG: Desglose detallado del cálculo de potencia para nueva tarifa:")
        
        # Usamos detección automática para determinar la unidad de cada precio de potencia
        # en lugar de asumir que están en un formato específico
        
        for period in client_data["power"].keys():
            power = client_data["power"].get(period, 0) or 0
            price = matching_rate["power_prices"].get(period) or 0
            
            if power is not None and price is not None and power > 0 and price > 0:
                # Convertir a precio diario usando detección automática
                daily_price = price_day(price, detect_power_price_unit(price))
                
                # Calcular coste mensual (30 días)
                period_cost = power * daily_price * 30
                new_power_cost += period_cost
                
                # Obtener la unidad detectada para mostrarla en el log
                detected_unit = detect_power_price_unit(price)
                print(f"DEBUG: Periodo {period}: Potencia {power} kW * Precio {price} €/kW/{detected_unit} = Precio diario {daily_price:.6f} €/kW/día * 30 días = {period_cost:.2f} €/mes")
        
        # No es necesario dividir por 12 y multiplicar por monthly_factor porque ya hemos calculado
        # el coste mensual correctamente usando el precio diario
        
        # Normalizar el coste de energía a un mes estándar de 30 días
        new_energy_cost = new_energy_cost * monthly_factor
        
        # El coste de potencia ya está normalizado a 30 días en el cálculo anterior
        # No es necesario aplicar de nuevo el factor mensual
        print(f"DEBUG: Coste de potencia ya normalizado a 30 días: {new_power_cost:.2f} €/mes")
        
        # Para energía reactiva, asumimos una reducción del 20% en comparación con la tarifa actual
        # ya que las tarifas de OLE optimizan el factor de potencia
        # Este valor se proyectará por factor 7 para el cálculo anual
        new_reactive_cost = current_reactive_cost * 0.8 if current_reactive_cost > 0 else 0
        print(f"DEBUG: Reducción del 20% en reactiva: {current_reactive_cost} € -> {new_reactive_cost} € al mes")
        
        # Cálculo del coste total con la mejor tarifa
        # Asegurar que todas las variables son numéricas antes de la suma
        new_energy_cost = new_energy_cost or 0
        new_power_cost = new_power_cost or 0
        new_reactive_cost = new_reactive_cost or 0
        
        new_total_cost = new_energy_cost + new_power_cost + new_reactive_cost
        
        # Cálculo del ahorro
        savings = current_total_cost - new_total_cost
        savings_percentage = (savings / current_total_cost) * 100 if current_total_cost > 0 else 0
        
        # Registrar los valores para debugging
        print(f"DEBUG: Periodo de facturación (días): {billing_period_days}")
        print(f"DEBUG: Factor mensual: {monthly_factor}")
        print(f"DEBUG: Energía actual: {current_energy_cost} €/mes")
        print(f"DEBUG: Potencia actual: {current_power_cost} €/mes")
        print(f"DEBUG: Reactiva actual: {current_reactive_cost} €/mes")
        print(f"DEBUG: Descuentos aplicados: {current_discounts} €/mes")
        print(f"DEBUG: Total actual: {current_total_cost} €/mes")
        print(f"DEBUG: Nueva energía: {new_energy_cost} €/mes")
        print(f"DEBUG: Nueva potencia: {new_power_cost} €/mes")
        print(f"DEBUG: Nueva reactiva: {new_reactive_cost} €/mes")
        print(f"DEBUG: Nuevo total: {new_total_cost} €/mes")
        
        # Los costos ya están calculados para el periodo de facturación
        # No hacemos proyecciones anuales aquí, solo devolvemos los valores del período
        
        # Añadir penalty de energía reactiva para la UI
        # En el caso de reactiva, la penalización es el coste actual, ya que se puede eliminar completamente
        reactive_energy_penalty = current_reactive_cost
        
        # Log detallado para entender la discrepancia en los cálculos
        print("\n==== DESGLOSE DETALLADO DEL CÁLCULO DE ENERGÍA REACTIVA ====")
        print(f"Coste total de reactiva extraído: {client_data.get('reactive_energy_total_cost')}")
        print(f"Datos de consumo reactivo: {client_data.get('reactive_energy')}")
        print(f"Precios de energía reactiva: {client_data.get('reactive_energy_prices')}")
        print(f"Períodos con datos: {list(client_data.get('reactive_energy', {}).keys())}")
        print(f"Períodos con precios: {[p for p, v in client_data.get('reactive_energy_prices', {}).items() if v]}")
        
        if client_data.get("reactive_energy") and not client_data.get("reactive_energy_total_cost"):
            print("\nCÁLCULO POR PERÍODO (usado debido a que no hay coste total extraído):")
            # Recalcular para mostrar el proceso completo
            total_recalculado = 0
            for period in client_data.get("reactive_energy", {}).keys():
                reactive = client_data.get("reactive_energy", {}).get(period, 0) or 0
                reactive_price = client_data.get("reactive_energy_prices", {}).get(period) or 0
                if reactive is not None and reactive_price is not None and reactive > 0 and reactive_price > 0:
                    period_cost = reactive * reactive_price
                    total_recalculado += period_cost
                    print(f"  - Período {period}: {reactive} kVArh * {reactive_price} €/kVArh = {period_cost} €")
                else:
                    print(f"  - Período {period}: No computado - Consumo: {reactive}, Precio: {reactive_price}")
            
            print(f"\nTotal sin normalizar (recalculado): {total_recalculado} €")
        
        print(f"Total sin normalizar (usado en cálculo): {raw_reactive_cost} €")
        print(f"Factor mensual aplicado: {monthly_factor} (30 días / {billing_period_days} días)")
        print(f"Total normalizado a mes estándar: {current_reactive_cost} €")
        print(f"NOTA: Este valor se proyectará por factor 7 en el cálculo de ahorro anual")
        print(f"Proyección anual (factor 7): {reactive_energy_penalty * 7} €")
        print(f"Valor redondeado para UI: {round(reactive_energy_penalty * 7)} €")
        print(f"Valor entero para UI: {int(round(reactive_energy_penalty * 7))} €")
        print("==== FIN DEL DESGLOSE ====\n")

        # Detectar exceso de potencia (pending)
        power_excess_penalty = 0
        
        return {
            "monthly_savings": round(savings, 2),  # Ahorro en el periodo de facturación
            "savings_percentage": round(savings_percentage, 2),  # Porcentaje de ahorro
            "current_energy_cost": round(current_energy_cost, 2),
            "current_power_cost": round(current_power_cost, 2),
            "current_reactive_cost": round(current_reactive_cost, 2),
            "current_total_cost": round(current_total_cost, 2),
            "new_energy_cost": round(new_energy_cost, 2),
            "new_power_cost": round(new_power_cost, 2),
            "new_reactive_cost": round(new_reactive_cost, 2),
            "new_total_cost": round(new_total_cost, 2),
            "reactive_energy_penalty": round(reactive_energy_penalty, 2),
            "power_excess_penalty": round(power_excess_penalty, 2)
        }
