from flask import render_template, redirect, url_for, flash, request, current_app, Blueprint, session, jsonify
from datetime import datetime
from app.forms import InvoiceUploadForm
from app.models import InvoiceLead
from app.services import send_lead_notification_email, send_savings_report_email
from werkzeug.utils import secure_filename
from flask_wtf.csrf import generate_csrf
import os
import json
import logging
from . import invoice_bp
from app.extensions import db

# Comprobar dependencias disponibles
DEPENDENCIAS_COMPLETAS = True
# Variable para controlar disponibilidad del LLM
LLM_AVAILABLE = False

try:
    # Importar dependencias principales
    from app.services.tariff_detector import TariffDetector, OCR_AVAILABLE
    from app.services.energy_calculator import EnergyCalculator, PANDAS_AVAILABLE
    from app.services.tariff_comparison import TariffComparison
    
    # Intentar importar cliente LLM para DeepSeek
    try:
        from app.services.llm_multi_provider import extract_with_llm
        LLM_AVAILABLE = True
        logging.info("Cliente LLM multi-proveedor cargado correctamente")
    except ImportError as e:
        logging.warning(f"Cliente LLM no disponible: {e}")
        LLM_AVAILABLE = False
    
    if not OCR_AVAILABLE:
        logging.warning("Módulos OCR no disponibles. La detección automática será limitada.")
        
    if not PANDAS_AVAILABLE:
        logging.warning("pandas/numpy no disponibles. Análisis de datos limitado.")
        
    # Si alguna dependencia falta, ajustar comportamiento
    DEPENDENCIAS_COMPLETAS = OCR_AVAILABLE and PANDAS_AVAILABLE
    
except ImportError as e:
    logging.error(f"Error al importar dependencias críticas: {str(e)}")
    DEPENDENCIAS_COMPLETAS = False
    # Definir clases ficticias para evitar errores
    TariffDetector = type('TariffDetector', (), {})
    EnergyCalculator = type('EnergyCalculator', (), {})

# Helper para verificar extensiones permitidas
def allowed_file(filename, allowed_extensions=None):
    """Verifica si el archivo es un tipo permitido"""
    if allowed_extensions is None:
        allowed_extensions = ['pdf', 'jpg', 'jpeg', 'png']
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in allowed_extensions

@invoice_bp.route('/estudio-gratuito', methods=['GET', 'POST'])
def estudio_gratuito():
    """Procesa las solicitudes de estudio de facturas gratuito"""
    current_app.logger.info(f"Request method: {request.method}")
    
    # Limpiar datos antiguos de la sesión cuando se carga la página
    if request.method == 'GET':
        # Conservar solo datos necesarios para manejo de usuario/sesión
        keys_to_keep = ['_fresh', '_user_id', 'csrf_token']
        keys_to_remove = [key for key in list(session.keys()) if key not in keys_to_keep]
        for key in keys_to_remove:
            session.pop(key, None)
        current_app.logger.info("Datos antiguos eliminados de la sesión")
    
    form = InvoiceUploadForm()
    detection_available = DEPENDENCIAS_COMPLETAS
    
    if form.validate_on_submit():
        current_app.logger.info(f"Form data: {request.form}")
        current_app.logger.info(f"Request files: {request.files}")
        
        # Limpiar datos de sesión anteriores al procesar un nuevo archivo
        keys_to_keep = ['_fresh', '_user_id', 'csrf_token']
        keys_to_remove = [key for key in list(session.keys()) if key not in keys_to_keep]
        for key in keys_to_remove:
            session.pop(key, None)
        current_app.logger.info("Datos antiguos eliminados de la sesión al iniciar nuevo procesamiento")
        
        # Verificar que tengamos al menos un archivo (factura PDF o foto)
        if 'invoice_file' not in request.files and 'invoice_photo' not in request.files:
            flash('No se seleccionó ningún archivo.', 'error')
            return redirect(request.url)
            
        # Determinar qué archivo usar (priorizar PDF si ambos están presentes)
        if 'invoice_file' in request.files and request.files['invoice_file'].filename:
            file = request.files['invoice_file']
            file_type = "pdf"
            current_app.logger.info(f"Procesando archivo PDF: {file.filename}")
        elif 'invoice_photo' in request.files and request.files['invoice_photo'].filename:
            file = request.files['invoice_photo']
            file_type = "photo"
            current_app.logger.info(f"Procesando foto de factura: {file.filename}")
        else:
            flash('Ningún archivo seleccionado.', 'error')
            current_app.logger.warning("No se encontró nombre de archivo válido.")
            return redirect(request.url)

        # Verificar extensión del archivo según tipo
        allowed_extensions = ['pdf'] if file_type == "pdf" else ['jpg', 'jpeg', 'png']
        if file and allowed_file(file.filename, allowed_extensions):
            filename = secure_filename(file.filename)
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            new_filename = f"{timestamp}_{filename}"
            
            # Guardar archivo en el sistema de archivos local
            uploads_dir = os.path.join(current_app.instance_path, 'uploads')
            
            # Crear el directorio si no existe
            if not os.path.exists(uploads_dir):
                os.makedirs(uploads_dir)
                
            file_path = os.path.join(uploads_dir, new_filename)
            file.save(file_path)
            
            # Para fotos, debemos convertirlas a PDF si queremos usar OCR
            if file_type == "photo":
                try:
                    from PIL import Image
                    import tempfile
                    
                    # Convertir la imagen a PDF para procesamiento
                    img = Image.open(file_path)
                    pdf_path = f"{file_path.rsplit('.', 1)[0]}.pdf"
                    
                    # Si la imagen está en RGB, convertirla a modo RGB para guardar como PDF
                    if img.mode != "RGB":
                        img = img.convert("RGB")
                    
                    img.save(pdf_path, "PDF")
                    current_app.logger.info(f"Imagen convertida a PDF: {pdf_path}")
                    
                    # Usar el PDF para detección
                    ocr_file_path = pdf_path
                except Exception as e:
                    current_app.logger.error(f"Error al convertir imagen a PDF: {str(e)}")
                    ocr_file_path = file_path  # Usar la imagen original como fallback
            else:
                ocr_file_path = file_path
            
            try:
                # Crear el lead en la base de datos
                lead = InvoiceLead(
                    name=form.name.data,
                    email=form.email.data,
                    phone=form.phone.data,  # Ya no es obligatorio
                    cups=None,  # Ya no solicitamos CUPS en el formulario inicial
                    file_path=file_path,
                    message=form.message.data if form.message.data else None,
                    rgpd_consent=form.rgpd_consent.data,
                    status='pending',
                    channel='public',  # Canal público
                    created_by_user_id=None  # No hay usuario registrado
                )
                db.session.add(lead)
                db.session.commit()
                
                # Ya no enviamos correo en el paso 1; se gestionará en el paso 3
                
                # Comprobar si tenemos todas las dependencias para la detección automática
                if detection_available:
                    try:
                        # Inicializar detector de peaje y procesar archivo
                        current_app.logger.info(f"Iniciando análisis de factura: {ocr_file_path}")
                        
                        # 1. Primero extraer el texto completo del PDF para ambos métodos
                        detector = TariffDetector()
                        extracted_text = detector._extract_text_from_pdf(ocr_file_path)
                        
                        # 2. Intentar extracción con LLM si está disponible
                        llm_result = None
                        if LLM_AVAILABLE:
                            current_app.logger.info("Intentando extracción con DeepSeek LLM...")
                            llm_result = extract_with_llm(extracted_text)
                            current_app.logger.info(f"Extracción LLM completada: {llm_result is not None}")
                        
                        # 3. Obtener resultado con detector regex tradicional
                        current_app.logger.info("Procesando con extractor regex tradicional...")
                        regex_result = detector.detect_from_text(extracted_text)
                        
                        # 4. Combinar resultados (LLM tiene prioridad, regex para completar datos faltantes)
                        if llm_result:
                            current_app.logger.info("Usando resultado LLM como base")
                            detection_result = llm_result
                            # Completar datos faltantes con regex
                            for key, value in regex_result.items():
                                if detection_result.get(key) in (None, "", {}):
                                    detection_result[key] = value
                                    current_app.logger.info(f"Completando campo '{key}' con datos regex")
                        else:
                            current_app.logger.info("Usando solo resultado regex")
                            detection_result = regex_result
                        
                        # Registrar en log los resultados obtenidos
                        current_app.logger.info(f"Datos extraídos: {list(detection_result.keys())}")
                        current_app.logger.info(f"CUPS detectado: {detection_result.get('cups', 'No detectado')}")
                        current_app.logger.info(f"Tarifa detectada: {detection_result.get('tariff', 'No detectada')}")
                        current_app.logger.info(f"Cliente: {detection_result.get('client_name', 'No detectado')}")
                        current_app.logger.info(f"Dirección: {detection_result.get('supply_address', 'No detectada')}")
                        current_app.logger.info(f"Periodos potencia: {detection_result.get('power_periods', {})}")
                        current_app.logger.info(f"Consumos energía: {detection_result.get('energy_consumption', {})}")
                        current_app.logger.info(f"Método usado: {'LLM+Regex' if llm_result else 'Solo Regex'}")
                        
                        # Guardar archivo con resultado combinado para diagnóstico
                        try:
                            with open('llm_extraction_results.json', 'w', encoding='utf-8') as f:
                                import json
                                json.dump({"llm": llm_result, "regex": regex_result, "final": detection_result}, f, indent=2, ensure_ascii=False)
                        except Exception as e:
                            current_app.logger.warning(f"No se pudo guardar archivo de diagnóstico: {e}")
                        
                        # Guardar los resultados en la sesión para procesamiento posterior
                        # Si se detectó un CUPS, actualizar el lead en la base de datos
                        if detection_result.get('cups'):
                            lead.cups = detection_result['cups']
                            db.session.commit()

                        # Guardar los resultados esenciales en la sesión para el siguiente paso
                        session['lead_id'] = lead.id
                        
                        # Mapear los campos de fechas del periodo de facturación
                        # La IA devuelve billing_start y billing_end pero la plantilla espera billing_start_date y billing_end_date
                        if 'billing_start' in detection_result and detection_result['billing_start']:
                            detection_result['billing_start_date'] = detection_result['billing_start']
                        
                        if 'billing_end' in detection_result and detection_result['billing_end']:
                            detection_result['billing_end_date'] = detection_result['billing_end']
                        
                        # Reducir el tamaño guardando solo los datos esenciales y eliminando el texto original
                        # que es muy grande y causa que la cookie de sesión exceda los límites
                        if 'original_text' in detection_result:
                            # Guardar solo las primeras 100 caracteres del texto para depuración
                            detection_result['original_text_preview'] = detection_result['original_text'][:100] + '...'
                            del detection_result['original_text']
                            
                        current_app.logger.info("Texto original reducido para evitar exceder el tamaño de sesión")
                        session['detection_result'] = detection_result
                        
                        # Almacenar datos completos en la base de datos asociados al lead para recuperarlos luego
                        lead.details = detection_result
                        db.session.commit()
                        
                        # Redirigir siempre a la página de confirmación para que el usuario valide los datos
                        return redirect(url_for('invoice.confirm_tariff'))
                    
                    except Exception as e:
                        current_app.logger.error(f"Error en la detección de peaje: {str(e)}")
                        detection_available = False  # Desactivar la detección si falla
                
                # Flujo alternativo cuando la detección automática no está disponible
                if not detection_available:
                    # Crear datos de detección simulada
                    mock_detection = {
                        'tariff': '2.0TD',  # Asumir 2.0TD como caso más común
                        'power_contracted': None,
                        'reliable_detection': False,
                        'original_text': ''
                    }
                    
                    # Guardar en sesión para usar en la confirmación manual
                    session['lead_id'] = lead.id
                    session['detection_result'] = mock_detection
                    
                    # Redirigir siempre a la selección manual de peaje
                    lead.status = 'needs_review'
                    db.session.commit()
                    
                    # Mostrar mensaje informativo y redirigir
                    flash('Gracias por tu factura. Por favor, confirma el tipo de peaje para continuar.', 'info')
                    return redirect(url_for('invoice.confirm_tariff'))
                
            except Exception as e:
                current_app.logger.error(f"Error al procesar el lead: {str(e)}")
                flash('Error al procesar tu solicitud. Por favor, inténtalo de nuevo más tarde.', 'error')
        else:
            flash('Tipo de archivo no permitido. Por favor, sube un archivo PDF.', 'error')
            
    current_app.logger.info("Renderizando plantilla estudio_gratuito.html")
    return render_template('invoice/estudio_gratuito.html', title="Comparador Gratuito", form=form)


@invoice_bp.route('/confirmar-peaje', methods=['GET', 'POST'])
def confirm_tariff():
    """Permite al usuario confirmar todos los datos extraídos de la factura"""
    # Verificar que exista una detección en la sesión
    current_app.logger.info("========== Entrando a confirm_tariff ===========")
    current_app.logger.info(f"Datos en sesión: {list(session.keys())}")
    
    # Verificar que estamos utilizando los datos de la sesión correctos
    lead_id = session.get('lead_id')
    if lead_id:
        current_app.logger.info(f"Lead ID en sesión: {lead_id}")
        lead = InvoiceLead.query.get(lead_id)
        if lead:
            current_app.logger.info(f"Lead encontrado: {lead.id} - {lead.name} - {lead.file_path}")
    
    if 'detection_result' not in session:
        current_app.logger.error("No se encontró detection_result en la sesión")
        flash('Se produjo un error en el procesamiento. Por favor, intenta de nuevo.', 'error')
        return redirect(url_for('invoice.estudio_gratuito'))
    
    # Obtener los datos de la sesión - deben ser los recientes de la nueva factura
    detection = session.get('detection_result', {})
    
    # Validar que los datos corresponden al lead actual
    lead_id = session.get('lead_id')
    lead = None
    if lead_id:
        lead = InvoiceLead.query.get(lead_id)
        if lead:
            file_name = os.path.basename(lead.file_path) if lead.file_path else "(sin archivo)"
            current_app.logger.info(f"Validando datos para lead ID={lead_id}, archivo={file_name}")
            # Verificar concordancia entre los datos detectados y el archivo actual
            detected_cups = detection.get('cups', 'No disponible')
            if lead.cups and lead.cups != detected_cups and detected_cups != 'No disponible':
                current_app.logger.warning(f"DISCREPANCIA: CUPS del lead ({lead.cups}) != CUPS detectado ({detected_cups})")
    
    # Registrar todos los datos para verificación
    current_app.logger.info(f"Datos detectados: {list(detection.keys())}")
    current_app.logger.info(f"CUPS: {detection.get('cups', 'No disponible')}")
    current_app.logger.info(f"Tarifa: {detection.get('tariff', 'No disponible')}")
    current_app.logger.info(f"Cliente: {detection.get('client_name', 'No disponible')}")
    current_app.logger.info(f"Potencia contratada: {detection.get('power_contracted', 'No disponible')}")
    current_app.logger.info(f"Periodos potencia: {detection.get('power_periods', {})}")
    current_app.logger.info(f"Consumos energía: {detection.get('energy_consumption', {})}")
    current_app.logger.info(f"Energía reactiva: {detection.get('reactive_energy', {})}")
    current_app.logger.info(f"Precios energía reactiva: {detection.get('reactive_energy_prices', {})}")
    
    if request.method == 'POST':
        current_app.logger.info("Procesando confirmación de datos de factura")
        
        # Actualizar datos generales
        detection['client_name'] = request.form.get('client_name', detection.get('client_name', ''))
        detection['cups'] = request.form.get('cups', detection.get('cups', ''))
        detection['supply_address'] = request.form.get('supply_address', detection.get('supply_address', ''))
        detection['provider_name'] = request.form.get('provider_name', detection.get('provider_name', ''))
        detection['invoice_date'] = request.form.get('invoice_date', detection.get('invoice_date', ''))
        detection['billing_start_date'] = request.form.get('billing_start_date', detection.get('billing_start_date', ''))
        detection['billing_end_date'] = request.form.get('billing_end_date', detection.get('billing_end_date', ''))
        detection['billing_days'] = request.form.get('billing_days', detection.get('billing_days', ''))

        # Actualizar el peaje seleccionado por el usuario
        selected_tariff = request.form.get('tariff_type')
        if selected_tariff in ['2.0TD', '3.0TD', '6.1TD']:
            detection['tariff'] = selected_tariff
            detection['reliable_detection'] = True  # Confirmado por el usuario
        else:
            current_app.logger.warning(f"Tipo de peaje no válido seleccionado: {selected_tariff}")
            flash('Tipo de peaje no válido. Se usará el detectado.', 'warning')

        # Función auxiliar para procesar valores numéricos
        def safe_float(value_str):
            if not value_str:
                return None
            try:
                return float(value_str.replace(',', '.'))
            except (ValueError, TypeError):
                return None

        # Procesar periodos de potencia, consumo, precios y energía reactiva
        for period_type in ['power_periods', 'energy_prices', 'energy_consumption', 'power_prices', 'reactive_energy', 'reactive_energy_prices']:
            # Inicializar el diccionario si no existe
            if period_type not in detection or detection[period_type] is None:
                detection[period_type] = {}
                
            # Procesar valores para P1-P6
            for period_key in ['P1', 'P2', 'P3', 'P4', 'P5', 'P6']:
                form_field_name = f"{period_type}_{period_key}"
                if form_field_name in request.form:
                    value = safe_float(request.form.get(form_field_name))
                    if value is not None:
                        detection[period_type][period_key] = value
                        current_app.logger.debug(f"Actualizado {period_type}.{period_key} = {value}")

        # Guardar todos los datos actualizados en la sesión
        session['detection_result'] = detection
        current_app.logger.info(f"Datos de factura actualizados y guardados en sesión: {detection.keys()}")
        
        # Actualizar el CUPS en la base de datos si ha cambiado
        lead_id = session.get('lead_id')
        if lead_id:
            lead = InvoiceLead.query.get(lead_id)
            if lead and lead.cups != detection.get('cups'):
                lead.cups = detection.get('cups')
                db.session.commit()
                current_app.logger.info(f"CUPS actualizado en BD para lead {lead_id}: {detection.get('cups')}")

        return redirect(url_for('invoice.calculate_savings'))
    
    # Asegurarse de que todos los tipos de datos existan en el diccionario
    for key in ['reactive_energy', 'reactive_energy_prices']:
        if key not in detection:
            detection[key] = {}
            
    # Detectar la unidad de precio de potencia (día, mes, año) basado en los valores
    power_price_unit = 'año'  # Valor por defecto
    
    # Función auxiliar para detectar la unidad de precio
    def detect_power_price_unit(price):
        if price > 10:  # Valores típicos de precios anuales (€/kW/año)
            return 'año'
        elif price > 0.5:  # Valores típicos de precios mensuales (€/kW/mes)
            return 'mes'
        else:  # Valores típicos de precios diarios (€/kW/día)
            return 'día'
    
    # Intentar detectar la unidad basándonos en el primer precio de potencia no nulo
    power_prices = detection.get('power_prices', {})
    for period, price in power_prices.items():
        if price is not None and price > 0:
            power_price_unit = detect_power_price_unit(price)
            current_app.logger.info(f"Unidad de precio de potencia detectada: €/kW/{power_price_unit} (basado en {period}: {price})")
            break
    
    # Añadir la unidad detectada a los datos que pasamos a la plantilla
    detection['power_price_unit'] = power_price_unit
    
    # Generar un token CSRF para el formulario
    csrf_token = generate_csrf()
    
    return render_template('invoice/confirm_tariff.html', 
                           title="Confirmar Datos de Factura", 
                           detection=detection,
                           csrf_token=csrf_token)


@invoice_bp.route('/calcular-ahorro', methods=['POST', 'GET'])
def calculate_savings():
    """Cálculo de ahorro basándonos en los datos de factura confirmados"""
    # Verificar que tengamos datos en la sesión para continuar
    lead_id = session.get('lead_id')
    detection_result = session.get('detection_result')
    
    if not lead_id or not detection_result:
        flash('Sesión expirada o datos faltantes. Por favor, comienza de nuevo.', 'error')
        return redirect(url_for('invoice.estudio_gratuito'))
    
    # Recuperar lead de la base de datos
    lead = InvoiceLead.query.get(lead_id)
    
    if not lead or not os.path.exists(lead.file_path):
        flash('No se encontró el archivo de factura asociado.', 'error')
        return redirect(url_for('invoice.estudio_gratuito'))
    
    current_app.logger.info(f"Iniciando cálculo de ahorro con datos de factura: {detection_result.keys()}")
    
    try:
        tariff_type = detection_result.get('tariff', '2.0TD')
        current_app.logger.info(f"Tipo de tarifa detectado/confirmado: {tariff_type}")
        
        # Diagnosticar las claves disponibles en los datos detectados
        current_app.logger.info(f"DIAGNOSTICO DE DATOS - Claves disponibles: {list(detection_result.keys())}")
        
        # Simplemente registramos si falta algún dato para fines de logging, pero SIEMPRE usaremos el cálculo real
        # Verificar consumos de energía (verificamos ambas claves posibles)
        if (not detection_result.get('energy_consumption') and not detection_result.get('consumption')) or \
           (len(detection_result.get('energy_consumption', {})) == 0 and len(detection_result.get('consumption', {})) == 0):
            current_app.logger.warning("Advertencia: Datos de consumo incompletos o faltantes")
        else:
            current_app.logger.info(f"Datos de consumo encontrados: {detection_result.get('energy_consumption') or detection_result.get('consumption')}")
        
        # Verificar potencias contratadas (verificamos ambas claves posibles)
        if (not detection_result.get('power_periods') and not detection_result.get('power_contracted') and not detection_result.get('power')) or \
           (len(detection_result.get('power_periods', {})) == 0 and len(detection_result.get('power_contracted', {})) == 0 and len(detection_result.get('power', {})) == 0):
            current_app.logger.warning("Advertencia: Datos de potencia incompletos o faltantes")
        else:
            current_app.logger.info(f"Datos de potencia encontrados: {detection_result.get('power_periods') or detection_result.get('power_contracted') or detection_result.get('power')}")
            
        # Verificar precios de energía
        if not detection_result.get('energy_prices') or len(detection_result.get('energy_prices', {})) == 0:
            current_app.logger.warning("Advertencia: Precios de energía incompletos o faltantes")
        else:
            current_app.logger.info(f"Precios de energía encontrados: {detection_result.get('energy_prices')}")
            
        # Verificar precios de potencia
        if not detection_result.get('power_prices') or len(detection_result.get('power_prices', {})) == 0:
            current_app.logger.warning("Advertencia: Precios de potencia incompletos o faltantes")
        else:
            current_app.logger.info(f"Precios de potencia encontrados: {detection_result.get('power_prices')}")
        
        # Siempre intentamos el cálculo real con los datos disponibles
        attempt_real_calculation = True
            
        # Obtener datos de potencia contratada
        power_periods = detection_result.get('power_periods', {})
        if not power_periods:
            current_app.logger.warning("No se encontraron datos de potencia contratada")

        # Convertir días de facturación a número si está disponible
        billing_days = 30  # Valor por defecto
        try:
            if detection_result.get('billing_days'):
                billing_days = int(detection_result.get('billing_days'))
            elif detection_result.get('billing_start_date') and detection_result.get('billing_end_date'):
                # Intentar calcular días de facturación a partir de fechas
                try:
                    from datetime import datetime
                    start_date = datetime.strptime(detection_result.get('billing_start_date'), '%d/%m/%Y')
                    end_date = datetime.strptime(detection_result.get('billing_end_date'), '%d/%m/%Y')
                    billing_days = (end_date - start_date).days + 1
                except Exception as e:
                    current_app.logger.warning(f"No se pudieron calcular días a partir de fechas: {e}")
        except (ValueError, TypeError) as e:
            current_app.logger.warning(f"Error al procesar días de facturación: {e}")
        
        current_app.logger.info(f"Días de facturación: {billing_days}")
        
        # Inicializar el servicio de comparación de tarifas
        tariff_comparison = TariffComparison()
        matching_rate = tariff_comparison.find_matching_rate(tariff_type)
        
        # Siempre intentamos el cálculo real, nunca modo demo
        attempt_real_calculation = True

        # Siempre usamos el calculador real
        if matching_rate:
            try:
                # Usar los datos de la factura extraídos y confirmados, que están en la sesión
                # Preparar datos de factura para el cálculo
                invoice_data = {
                    'energy_prices': detection_result.get('energy_prices', {}),
                    'power_periods': power_periods,
                    'energy_consumption': detection_result.get('energy_consumption', {}),
                    # Usar precios de potencia si existen
                    'power_prices': detection_result.get('power_prices', {}),
                    'billing_days': billing_days,
                    'total_cost': 0  # No utilizado por ahora
                }
                
                # No hacemos verificaciones estrictas, usamos los datos que tenemos disponibles
                current_app.logger.info("Procediendo con el cálculo usando los datos disponibles")
                    
                current_app.logger.info(f"Datos preparados para cálculo: {invoice_data.keys()}")                
                current_app.logger.debug(f"Energy prices: {invoice_data['energy_prices']}")                
                current_app.logger.debug(f"Power periods: {invoice_data['power_periods']}")                
                current_app.logger.debug(f"Energy consumption: {invoice_data['energy_consumption']}")                
                
                # Preparar estructura para el servicio de comparación de tarifas
                client_data = {
                    "current_energy_prices": invoice_data.get('energy_prices', {}),
                    "current_power_prices": invoice_data.get('power_prices', {}),
                    "consumption": invoice_data.get('energy_consumption', {}),
                    "power": invoice_data.get('power_periods', {}),
                    "billing_period_days": invoice_data.get('billing_days', 30),
                    # Añadir datos de energía reactiva y precios
                    "reactive_energy": detection_result.get('reactive_energy', {}),
                    "reactive_energy_prices": detection_result.get('reactive_energy_prices', {}),
                    # Añadir el coste total de energía reactiva extraído directamente de la factura
                    "reactive_energy_total_cost": detection_result.get('reactive_energy_total_cost')
                }
                
                # Log para depuración de datos de energía reactiva
                if detection_result.get('reactive_energy') or detection_result.get('reactive_energy_total_cost'):
                    current_app.logger.info(f"Datos de energía reactiva incluidos en el cálculo: {detection_result.get('reactive_energy')}")
                    current_app.logger.info(f"Precios de energía reactiva incluidos en el cálculo: {detection_result.get('reactive_energy_prices')}")
                    current_app.logger.info(f"Coste total de energía reactiva extraído: {detection_result.get('reactive_energy_total_cost')}")
                    if detection_result.get('reactive_energy_total_cost'):
                        current_app.logger.info("USANDO COSTE TOTAL EXTRAÍDO para el cálculo de energía reactiva")
                    else:
                        current_app.logger.info("Calculando coste de reactiva por períodos (no hay coste total extraído)")
                
                
                # Calcular el ahorro usando nuestro servicio de comparación
                current_app.logger.info("Iniciando cálculo de ahorro con TariffComparison")
                savings_data = tariff_comparison.calculate_savings(client_data, matching_rate)
                
                if savings_data:
                    current_app.logger.info(f"Cálculo exitoso: {savings_data.keys()}")
                    # Usamos los costes mensuales para la comparación en la UI
                    # Nota: current_total_cost ya es mensual porque es el coste del período de facturación
                    current_cost = savings_data.get('current_total_cost', 0)
                    ole_cost = savings_data.get('new_total_cost', 0)
                    monthly_savings = max(current_cost - ole_cost, 0)  # Evitar valores negativos
                    # Calcular porcentaje solo si hay ahorro
                    savings_percent = (monthly_savings / current_cost * 100) if current_cost > 0 else 0
                    
                    # Extraer valor de penalización por energía reactiva (si existe)
                    reactive_energy_penalty = savings_data.get('reactive_energy_penalty', 0) or 0
                    power_excess_penalty = detection_result.get('power_excess_penalty', 0) or 0
                    
                    try:
                        if reactive_energy_penalty is None:
                            reactive_energy_penalty = 0
                        else:
                            reactive_energy_penalty = float(reactive_energy_penalty)
                    except (ValueError, TypeError):
                        reactive_energy_penalty = 0
                        
                    current_app.logger.info(f"Valor final de reactive_energy_penalty: {reactive_energy_penalty}")
                        
                    try:
                        if power_excess_penalty is None:
                            power_excess_penalty = 0
                        else:
                            power_excess_penalty = float(power_excess_penalty)
                    except (ValueError, TypeError):
                        power_excess_penalty = 0
                    
                    results = {
                        'yearly_savings': (monthly_savings / billing_days) * 365 if monthly_savings > 0 else 0,
                        'savings_percentage': savings_percent,
                        'monthly_savings': monthly_savings,
                        'estimation_reliability': 'Personalizado',
                        'call_recommended': savings_data.get('savings_percentage', 0) > 5,
                        'current_cost': current_cost,
                        'ole_cost': ole_cost,
                        'best_rate_name': matching_rate.get('name', ''),
                        'best_rate_supplier': matching_rate.get('supplier', ''),
                        'tariff_type': tariff_type,
                        'reactive_energy_penalty': reactive_energy_penalty,
                        'power_excess_penalty': power_excess_penalty,
                        'power_price_unit': detection_result.get('power_price_unit', 'año'),  # Conservar la unidad de precio de potencia
                        'demo_mode': False
                    }
                    
                    # Log adicional de depuración para verificar qué se está pasando a la plantilla
                    current_app.logger.info(f"Valores pasados a la plantilla - reactive_penalty: {results['reactive_energy_penalty']}")
                    current_app.logger.info(f"Proyección anual (factor 7) en plantilla: {results['reactive_energy_penalty'] * 7} €")
                else:
                    raise ValueError("El servicio de comparación no devolvió datos de ahorro válidos")
                    
            except Exception as e:
                error_message = str(e)
                current_app.logger.error(f"Error en el cálculo de ahorro real: {error_message}")
                
                # Si falla el cálculo real, caemos al modo demo
                current_app.logger.warning("Cambiando a modo de demostración debido a error en cálculo real")
                attempt_real_calculation = False  # Usar modo demo para esta solicitud
                
                # Información básica del error, sin ningún dato que requiera datetime
                session['calculation_error'] = {
                    'message': error_message,
                    'missing_data': [],
                }
                
                # Analizar el mensaje de error para identificar datos faltantes
                if 'datos de consumo' in error_message.lower():
                    session['calculation_error']['missing_data'].append('consumption')
                if 'datos de potencia' in error_message.lower():
                    session['calculation_error']['missing_data'].append('power')
                
                # Registrar detalles técnicos para depuración
                current_app.logger.debug(f"Datos en sesión durante error: {list(detection_result.keys())}")
                current_app.logger.debug(f"Energy: {detection_result.get('energy_consumption', {})}")
                current_app.logger.debug(f"Power: {detection_result.get('power_periods', {})}")
                current_app.logger.debug(f"Energy prices: {detection_result.get('energy_prices', {})}")
                current_app.logger.debug(f"Power prices: {detection_result.get('power_prices', {})}")
                
        # Si no hay resultados o falló el cálculo, usamos valores mínimos pero realistas
        if 'results' not in locals() or not results:
            current_app.logger.warning("No se pudo realizar el cálculo real, usando valores mínimos")
            
            # Crear resultados básicos con valores mínimos pero realistas basados en los datos disponibles
            # Calculamos los valores de manera simple pero realista
            power_cost = sum(detection_result.get('power_prices', {}).get(p, 0) * detection_result.get('power_periods', {}).get(p, 0) / 12 
                            for p in detection_result.get('power_periods', {}).keys())
            energy_cost = sum(detection_result.get('energy_prices', {}).get(p, 0) * detection_result.get('energy_consumption', {}).get(p, 0) 
                            for p in detection_result.get('energy_consumption', {}).keys())
            
            # Calcular un ahorro moderado y realista (8-10% del costo total)
            total_cost = max(power_cost + energy_cost, 100)  # Aseguramos un mínimo
            ole_cost = total_cost * 0.9  # Asumimos un 10% de ahorro
            monthly_savings = total_cost - ole_cost
            
            # Valores de penalización para el demo (pequeños, para no alarmar sin datos reales)
            demo_reactive_penalty = 0  # Sin penalización en modo demo por defecto
            demo_power_excess = 0  # Sin penalización en modo demo por defecto
            
            results = {
                'yearly_savings': monthly_savings * 12,  # Proyectamos a año multiplicando por 12
                'savings_percentage': 10.0,  # Porcentaje de ahorro conservador
                'monthly_savings': monthly_savings,
                'estimation_reliability': 'Calculado',
                'call_recommended': True,
                'current_cost': total_cost,
                'ole_cost': ole_cost,
                'tariff_type': tariff_type,
                'reactive_energy_penalty': demo_reactive_penalty,
                'power_excess_penalty': demo_power_excess,
                'power_price_unit': 'año',  # Valor por defecto para el demo
                'demo_mode': False  # Nunca indicamos que es modo demo
            }
        
        # Almacenar resultados en la sesión para mostrarlos
        session['savings_results'] = results
        
        # Almacenar los datos de factura confirmados y los resultados para mostrarlos en la vista final
        session['confirmed_invoice_data'] = {
            'client_name': detection_result.get('client_name', ''),
            'cups': detection_result.get('cups', ''),
            'supply_address': detection_result.get('supply_address', ''),
            'provider_name': detection_result.get('provider_name', ''),
            'tariff_type': tariff_type,
            'billing_period': f"{detection_result.get('billing_start_date', '')} a {detection_result.get('billing_end_date', '')}",
            'billing_days': billing_days
        }
        
        # Actualizar estado y detalles del lead
        lead.status = 'completed'
        
        # Crear un objeto detallado con todos los datos relevantes
        lead_details = {
            # Datos básicos
            'detected_tariff': tariff_type,
            'cups': detection_result.get('cups', ''),
            'client_name': detection_result.get('client_name', ''),
            'supply_address': detection_result.get('supply_address', ''),
            'provider_name': detection_result.get('provider_name', ''),
            
            # Periodo de facturación
            'billing_start': detection_result.get('billing_start_date', ''),
            'billing_end': detection_result.get('billing_end_date', ''),
            'billing_days': billing_days,
            
            # Datos técnicos
            'power_periods': detection_result.get('power_periods', {}),
            'energy_consumption': detection_result.get('energy_consumption', {}),
            'energy_prices': detection_result.get('energy_prices', {}),
            'power_prices': detection_result.get('power_prices', {}),
            
            # Resultados del cálculo
            'estimated_savings': results['yearly_savings'],
            'savings_percent': results['savings_percentage'],
            'best_rate_name': results.get('best_rate_name',''),
            'best_rate_supplier': results.get('best_rate_supplier',''),
            'monthly_savings': results.get('monthly_savings', results['yearly_savings'] / 12),
            'reliability': results['estimation_reliability'],
            'current_cost': results.get('current_cost', 0),
            'ole_cost': results.get('ole_cost', 0),
            'demo_mode': results.get('demo_mode', False),
            
            # Metadatos del procesamiento
            'calculation_timestamp': 'Calculado', # Simplificado para evitar problemas con datetime
            'calculation_version': '1.1'
        }
        
        # Guardar los detalles completos y marcar como completado
        # No usamos json.dumps aquí porque el setter de details ya maneja la serialización
        lead.details = lead_details  # Pasar directamente el diccionario
        lead.status = 'completed'
        
        # Agregar el estudio inicial al histórico
        from datetime import datetime
        initial_study = {
            'timestamp': datetime.now().isoformat(),
            'user_id': None,
            'user_name': 'Sistema',
            'action': 'estudio_inicial',
            'tariff_type': tariff_type,
            'estimated_savings': results['yearly_savings'],
            'best_plan': results.get('best_rate_name', 'No disponible'),
            'current_supplier': detection_result.get('provider_name', 'Desconocida'),
            'consumption_data': detection_result.get('energy_consumption', {}),
            'power_data': detection_result.get('power_periods', {}),
            'monthly_savings': results.get('monthly_savings', 0),
            'savings_percentage': results['savings_percentage']
        }
        lead.add_savings_study(initial_study)
        
        current_app.logger.info(f"Guardando detalles del lead {lead.id} para CUPS: {lead_details['cups']}")
        current_app.logger.info(f"Agregado estudio inicial al histórico: {initial_study}")
        db.session.commit()
        
        # Enviar notificación por correo electrónico con los resultados
        try:
            current_app.logger.info(f"Enviando correo con resultados para lead {lead.id}")
            send_savings_report_email(lead, results, lead.file_path)
            current_app.logger.info(f"Correo enviado exitosamente para lead {lead.id}")
        except Exception as mail_error:
            current_app.logger.error(f"Error al enviar correo: {str(mail_error)}")
            # El error en el correo no debería detener el proceso para el usuario
        
        # Redirigir a página de resultados
        return redirect(url_for('invoice.saving_result'))
        
    except Exception as e:
        current_app.logger.error(f"Error en el cálculo de ahorro: {str(e)}")
        flash('Error al procesar los cálculos. Por favor, intenta de nuevo más tarde.', 'error')
        return redirect(url_for('invoice.estudio_gratuito'))


@invoice_bp.route('/resultado-ahorro')
def saving_result():
    """Muestra los resultados del cálculo de ahorro"""
    # Verificar que existan resultados en la sesión
    if 'savings_results' not in session or 'lead_id' not in session:
        flash('Se produjo un error en el procesamiento. Por favor, intenta de nuevo.', 'error')
        return redirect(url_for('invoice.estudio_gratuito'))
    
    # Obtener datos de la sesión
    results = session.get('savings_results', {})
    invoice_data = session.get('confirmed_invoice_data', {})
    lead_id = session.get('lead_id')
    
    current_app.logger.info(f"Mostrando resultados para lead_id: {lead_id}, ahorro: {results.get('yearly_savings')}")
    current_app.logger.debug(f"Datos de factura confirmados: {invoice_data.keys() if invoice_data else 'No disponibles'}")
    
    try:
        # Recuperar el lead
        lead = InvoiceLead.query.get(lead_id)
        
        # Comprobar si estamos en modo demostración
        demo_mode = results.get('demo_mode', False)
        
        # Obtener datos adicionales de la factura
        client_name = invoice_data.get('client_name', '')
        supply_address = invoice_data.get('supply_address', '')
        provider_name = invoice_data.get('provider_name', '')
        billing_period = invoice_data.get('billing_period', '')
        billing_days = invoice_data.get('billing_days', 30)
        
        # Calcular ahorro mensual para mostrar
        monthly_savings = results.get('yearly_savings', 0) / 12
        
        # Obtener valores de penalización por reactiva y exceso de potencia
        reactive_energy_penalty = results.get('reactive_energy_penalty', 0)
        power_excess_penalty = results.get('power_excess_penalty', 0)
        
        # IMPORTANTE: Aplicar factor de proyección anual (factor 7) al valor de la penalización de energía reactiva
        # Esta proyección refleja el ahorro anual estimado por reducción/eliminación de reactiva
        reactive_energy_annual_penalty = reactive_energy_penalty * 7 if reactive_energy_penalty is not None else 0
        
        # Log para depuración de valores
        current_app.logger.info(f"Valor mensual de reactive_energy_penalty: {reactive_energy_penalty}")
        current_app.logger.info(f"Valor anual proyectado (factor 7) que se pasa a la plantilla: {reactive_energy_annual_penalty}")
        
        return render_template('invoice/saving_result.html',
                               title="Tu Ahorro Estimado",
                               yearly_savings=results.get('yearly_savings', 0),
                               monthly_savings=monthly_savings,
                               savings_percentage=results.get('savings_percentage', 0),
                               reliability=results.get('estimation_reliability', 'Estimado'),
                               call_recommended=results.get('call_recommended', False),
                               current_cost=results.get('current_cost', 0),
                               ole_cost=results.get('ole_cost', 0),
                               tariff_type=results.get('tariff_type', '2.0TD'),
                               reactive_energy_penalty=reactive_energy_annual_penalty,
                               power_excess_penalty=power_excess_penalty,
                               power_price_unit=results.get('power_price_unit', 'año'),  # Pasar la unidad de precio de potencia
                               demo_mode=demo_mode,
                               lead=lead,
                               client_name=client_name,
                               supply_address=supply_address,
                               provider_name=provider_name,
                               billing_period=billing_period,
                               billing_days=billing_days)
    except Exception as e:
        current_app.logger.error(f"Error al mostrar resultados: {str(e)}")
        flash('Error al mostrar los resultados. Por favor, contacta con nuestro equipo.', 'error')
        return redirect(url_for('public.home'))


@invoice_bp.route('/solicitar-llamada', methods=['POST'])
def request_call():
    """Procesa la solicitud de llamada del usuario"""
    if 'lead_id' not in session:
        return jsonify({'success': False, 'message': 'No se encontró tu solicitud.'})
        
    lead_id = session['lead_id']
    
    try:
        lead = InvoiceLead.query.get(lead_id)
        if not lead:
            return jsonify({'success': False, 'message': 'No se encontró tu solicitud en nuestra base de datos.'})
        
        # Actualizar el estado del lead para indicar que quiere ser contactado
        lead.status = 'callback_requested'
        db.session.commit()
        
        # Aquí se podría enviar una notificación al equipo de ventas
        
        return jsonify({
            'success': True,
            'message': 'Recibido. Nuestro equipo te llamará en las próximas 24 horas.'
        })
    except Exception as e:
        current_app.logger.error(f"Error al procesar solicitud de llamada: {str(e)}")
        return jsonify({
            'success': False,
            'message': 'Error al procesar tu solicitud. Por favor, llámanos directamente.'
        })
