import os

# Obtener la ruta base correcta (ajustar esto según tu estructura)
ruta_proyecto = "C:\\Elephant_D102"  # Ruta absoluta a la raíz del proyecto

# Configuración de la tabla (estas variables se usarán en el contenido)
table_name = "CZE_BreakerProtectionType"
id_field = "Item_breaker_Protection"
technical_sheet = "TechnicalSheet"
item_breaker = "Item_breaker"
nombre_funcion = "BreakerProtectionType"
NombreModulo = "Catálogo de tipos de protección de interruptores"

# Campos adicionales para hacer el código más dinámico
description_field = "description"
price_field = "price"
type_field = "TypeBreaker"  # Nombre del campo que contiene el tipo en la base de datos

print("=== Generando archivos para el módulo:", NombreModulo, "===")
print(f"Función: {nombre_funcion}")
print(f"Tabla: {table_name}")
print(f"Campo ID: {id_field}")
print("===================================")

#==============================================================================
# ARCHIVO 1: Archivo SQL Optimizado
#==============================================================================
# Ruta relativa desde la raíz del proyecto para el archivo SQL
ruta_relativa = os.path.join("src", "Consultas_SQL", "Ventas", "VentasEUA", "DataMaster")
ruta_completa = os.path.join(ruta_proyecto, ruta_relativa)
nombre_archivo_sql = f"DM{nombre_funcion}OptimizadoSQL.py"
ruta_archivo_sql = os.path.join(ruta_completa, nombre_archivo_sql)

# Crear los directorios si no existen
os.makedirs(ruta_completa, exist_ok=True)

# Contenido del archivo SQL
contenido_sql = f'''# Archivo: DM{nombre_funcion}OptimizadoSQL.py
# Ruta: src\\Consultas_SQL\\Ventas\\VentasEUA\\DataMaster\\DM{nombre_funcion}OptimizadoSQL.py
# Lenguaje: Python
from Consultas_SQL.conexion import get_connection

# Configuración para la tabla de breakers
TABLE_NAME = "{table_name}"
ID_FIELD = "{id_field}"
DESCRIPTION_FIELD = "{description_field}"
PRICE_FIELD = "{price_field}"
TYPE_FIELD = "{type_field}"
TECHNICAL_SHEET_FIELD = "{technical_sheet}"

# Mapeo de campos para acceso coherente
FIELD_MAPPING = {{
    "id": ID_FIELD,
    "description": DESCRIPTION_FIELD,
    "price": PRICE_FIELD,
    "type": TYPE_FIELD,
    "technical_sheet": TECHNICAL_SHEET_FIELD
}}

def get_{nombre_funcion}():
    """Obtiene los {nombre_funcion} desde la base de datos."""
    query = f"""
    SELECT *
    FROM {{TABLE_NAME}}
    """
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        cursor = conn.cursor()
        cursor.execute(query)
        
        # Obtener los nombres de las columnas de la tabla
        column_names = [column[0] for column in cursor.description]
        
        # Obtener los resultados
        results = cursor.fetchall()
        
        # Construir dinámicamente la lista de diccionarios
        items = []
        for row in results:
            item_dict = {{}}
            for i, column_name in enumerate(column_names):
                item_dict[column_name] = row[i]
            items.append(item_dict)
            
        return items
    except Exception as e:
        print(f"[Error inesperado] Error obteniendo {nombre_funcion}: {{e}}")
        return []
    finally:
        if conn:
            conn.close()
            
def item_exists(item_id):
    """
    Verifica si un {nombre_funcion} existe en la base de datos.
    
    Args:
        item_id: Valor del campo ID a buscar
        
    Returns:
        tuple: Resultado de la consulta COUNT(*)
    """
    query = f"""
    SELECT COUNT(*) 
    FROM {{TABLE_NAME}} 
    WHERE {{ID_FIELD}} = ?
    """
    conn = get_connection()
    try:
        cursor = conn.cursor()
        cursor.execute(query, (item_id,))
        result = cursor.fetchone()
        return result
    finally:
        if conn:
            conn.close()

def insert_{nombre_funcion}(data_dict):
    """
    Inserta un nuevo {nombre_funcion} en la base de datos.
    
    Args:
        data_dict (dict): Diccionario con los campos y valores a insertar
    """
    # Obtener los campos y valores del diccionario
    fields = list(data_dict.keys())
    values = list(data_dict.values())
    
    # Crear los placeholders para la consulta SQL (?, ?, ?)
    placeholders = ", ".join(["?"] * len(fields))
    
    # Construir la consulta SQL
    query = f"""
    INSERT INTO {{TABLE_NAME}} ({{", ".join(fields)}})
    VALUES ({{placeholders}})
    """
    
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        cursor = conn.cursor()
        cursor.execute(query, values)
        conn.commit()
    except Exception as e:
        print(f"[Error inesperado] Error insertando {nombre_funcion}: {{e}}")
    finally:
        if conn:
            conn.close()            

def update_{nombre_funcion}(data_dict):
    """
    Actualiza un {nombre_funcion} en la base de datos.
    
    Args:
        data_dict (dict): Diccionario con los campos y valores a actualizar.
                        Debe incluir el ID_FIELD para identificar el registro.
    """
    # Verificar que el ID está presente en el diccionario
    if ID_FIELD not in data_dict:
        print(f"[Error] El campo ID '{{ID_FIELD}}' es obligatorio para actualizar")
        return
    
    # Extraer el ID y eliminarlo del diccionario de datos
    id_value = data_dict.pop(ID_FIELD)
    
    # Si no hay campos para actualizar, salir
    if not data_dict:
        print("[Error] No se proporcionaron campos para actualizar")
        return
    
    # Construir la cláusula SET para la actualización
    set_clause = ", ".join([f"{{field}} = ?" for field in data_dict.keys()])
    
    # Valores para la consulta (valores para SET + valor para WHERE)
    values = list(data_dict.values())
    values.append(id_value)  # Añadir el ID al final
    
    query = f"""
    UPDATE {{TABLE_NAME}}
    SET {{set_clause}}
    WHERE {{ID_FIELD}} = ?
    """
    
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        cursor = conn.cursor()
        cursor.execute(query, values)
        conn.commit()
    except Exception as e:
        print(f"[Error inesperado] Error actualizando registro en {{TABLE_NAME}}: {{e}}")
    finally:
        if conn:
            conn.close()
            
def delete_{nombre_funcion}(item_id):
    """
    Elimina un {nombre_funcion} de la base de datos.
    
    Args:
        item_id: Valor del ID del {nombre_funcion} a eliminar
    """
    query = f"""
    DELETE FROM {{TABLE_NAME}}
    WHERE {{ID_FIELD}} = ?
    """
    
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        cursor = conn.cursor()
        cursor.execute(query, (item_id,))
        conn.commit()
    except Exception as e:
        print(f"[Error inesperado] Error eliminando {nombre_funcion}: {{e}}")
    finally:
        if conn:
            conn.close()

def searchitems():
    """
    Obtiene los IDs y hojas técnicas de todos los {nombre_funcion}.
    
    Returns:
        list: Lista de diccionarios con ID y TechnicalSheet
    """
    query = f"""
    SELECT {{ID_FIELD}}, {{TECHNICAL_SHEET_FIELD}} 
    FROM {{TABLE_NAME}}
    """
    
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        cursor = conn.cursor()
        cursor.execute(query)
        results = cursor.fetchall()
        
        if not results:
            return []
        
        # Construir lista de diccionarios con campos específicos
        items = []
        for row in results:
            items.append({{
                ID_FIELD: row[0],
                TECHNICAL_SHEET_FIELD: row[1]
            }})
            
        return items
    except Exception as e:
        print(f"[Error inesperado] Error consultando {nombre_funcion}: {{e}}")
        return []
    finally:
        if conn:
            conn.close()

def update_{nombre_funcion}_{technical_sheet}(item, TechnicalSheet):
    """
    Actualiza la hoja técnica de un {nombre_funcion}.
    
    Args:
        item: ID del {nombre_funcion}
        TechnicalSheet: Nueva URL de la hoja técnica
    """
    query = f"""
    UPDATE {{TABLE_NAME}}
    SET {{TECHNICAL_SHEET_FIELD}} = ?
    WHERE {{ID_FIELD}} = ?
    """
    
    try:
        conn = get_connection()
        if not conn:
            raise ConnectionError("No se pudo establecer conexión con la base de datos")
        
        cursor = conn.cursor()
        cursor.execute(query, (TechnicalSheet, item))
        conn.commit()
    except Exception as e:
        print(f"[Error inesperado] Error actualizando hoja técnica: {{e}}")
    finally:
        if conn:
            conn.close()'''

# Escribir el contenido en el archivo SQL
with open(ruta_archivo_sql, 'w', encoding='utf-8') as archivo:
    archivo.write(contenido_sql)

print(f"[1/4] Archivo SQL creado exitosamente en: {ruta_archivo_sql}")

#==============================================================================
# ARCHIVO 2: Archivo Flask Optimizado
#==============================================================================
# Ruta relativa desde la raíz del proyecto para el archivo Flask
ruta_relativa_app = os.path.join("src", "App", "Ventas_Module", "VentasEUA", "DataMaster")
ruta_completa_app = os.path.join(ruta_proyecto, ruta_relativa_app)
nombre_archivo_app = f"DM{nombre_funcion}Optimizado.py"
ruta_archivo_app = os.path.join(ruta_completa_app, nombre_archivo_app)
ArchivoSQLOptimizado = f"DM{nombre_funcion}OptimizadoSQL"

# Crear los directorios si no existen
os.makedirs(ruta_completa_app, exist_ok=True)

# Contenido del archivo Flask
contenido_app = f'''# Optimized Python Code
# Archivo: DM{nombre_funcion}Optimizado.py
# Ruta: src\\App\\Ventas_Module\\VentasEUA\\DataMaster\\DM{nombre_funcion}Optimizado.py
# Lenguaje: Python con Flask

from flask import Flask, render_template, send_file, abort, url_for, jsonify, request, session
import os
from dotenv import load_dotenv
import ftplib
from werkzeug.utils import secure_filename
import tempfile
from App.Security_Module.UserPassword import user_has_access
from App.Subir_Archivo import subir_archivo_ftp_desde_request, view_pdf_file, verificar_actualizar_archivos

# Importar funciones y configuraciones del módulo SQL optimizado
from Consultas_SQL.Ventas.VentasEUA.DataMaster.{ArchivoSQLOptimizado} import (
    get_{nombre_funcion}, insert_{nombre_funcion}, update_{nombre_funcion}, 
    delete_{nombre_funcion}, item_exists, searchitems, 
    update_{nombre_funcion}_{technical_sheet}, FIELD_MAPPING
)

import requests
import os

# Cargar las variables de entorno desde el archivo .env
load_dotenv()

# /////////////////////////////////
#   Variables Generales
# ////////////////////////////////
ruta_pdf = "/domains/sycelephant.com/public_html/file/Ventas/EUA/DMITEMS/BREAKER_PROTECTION_TYPE"
carpeta = "/Ventas/EUA/DMITEMS/BREAKER_PROTECTION_TYPE/"
NombreModulo = "{NombreModulo}"

# /////////////////////////////////
#   Funciones Genericas
# ////////////////////////////////

# /////////////////////////////////
#   Varifica items Repetidos
# ////////////////////////////////
def existen_items(item):
    result = item_exists(item)
    if result:  # Verifica si result no es None (puede ser None si la consulta no encuentra nada)
        count = int(result[0]) # Accede al primer elemento de la tupla (índice 0) y lo convierte a entero.
    else:
        count = 0
    return count

# /////////////////////////////////
#   Funciones dedicadas
# ////////////////////////////////

def register_dm{nombre_funcion}Optimizado_routes(app):
    
    # /////////////////////////////////
    #   Consultar Breakers
    # ////////////////////////////////
    @app.route("/ventas/data_master/{nombre_funcion}/list{nombre_funcion}", methods=["GET"])
    def list_{nombre_funcion}():
        """
        user_id = session.get('user_id')
        if not user_has_access(user_id, "Ventas","read"):
            message = "No tiene permisos para visualizar este archivo"
            return render_template("Security/AccessDened.html",message)
        """
        #Retorna la lista de get_{nombre_funcion}.
        try:
            items = get_{nombre_funcion}()
            # Agregar la información de mapeo de campos para el frontend
            response_data = {{
                "data": items,
                "fieldMapping": FIELD_MAPPING
            }}
            return jsonify(response_data), 200
        except Exception as e:
            return jsonify({{"error": str(e)}}), 500
    
    # /////////////////////////////////
    #   Vista principal del módulo
    # ////////////////////////////////
    @app.route("/ventas/data_master/{nombre_funcion}")
    def view_{nombre_funcion}():
        """Renderiza la plantilla principal del módulo."""
        return render_template("Ventas/VentasEUA/DataMaster/DM{nombre_funcion}Optimizado.html")
    
    # Función para visualizar PDF
    @app.route("/ventas/pdf_viewer{nombre_funcion}/view/<path:filename_or_url>")
    def ver_pdf{nombre_funcion}(filename_or_url):
        """Endpoint para visualizar un archivo PDF específico."""
        return view_pdf_file(filename_or_url)
    
    # /////////////////////////////////
    #   Guardar Breakers
    # ////////////////////////////////
    @app.route("/ventas/data_master/brakers/add{nombre_funcion}", methods=["POST"])
    def add_{nombre_funcion}():
        user_id = session.get('user_id')
        try:
            # Obtener datos del formulario
            item = request.form.get("item")
            description = request.form.get("description")
            price = request.form.get("price")
            type = request.form.get("type")
            technicalSheet = request.files.get("TechnicalSheet")
            
            if not all([item, description, price, type]): 
                return jsonify({{"error": "Todos los campos son obligatorios."}}), 400
            
            # Verificar si el item ya existe
            if existen_items(item) == 1:
                return jsonify({{"error": "El item ya existe en el catálogo."}}), 400
            
            filename = None
            if technicalSheet:
                resultado = subir_archivo_ftp_desde_request(technicalSheet, item, ruta_pdf, carpeta)
                
                if resultado["exito"]:
                    filename = resultado["url_web"]
                else:
                    return jsonify({{"error": resultado["mensaje"]}}), 400
            
            # Database Insert - utilizando el método que acepta diccionario y el mapeo de campos
            data_dict = {{
                FIELD_MAPPING["id"]: item,
                FIELD_MAPPING["description"]: description,
                FIELD_MAPPING["price"]: price,
                FIELD_MAPPING["type"]: type,
                FIELD_MAPPING["technical_sheet"]: filename
            }}
            
            # Llamar a la función de inserción
            insert_{nombre_funcion}(data_dict)
            
            return jsonify({{"message": "{nombre_funcion} agregado exitosamente."}}), 201
        
        except Exception as e:
            return jsonify({{"error": str(e)}}), 500
    
    # /////////////////////////////////
    #   Actualizar inputs de Breakers
    # ////////////////////////////////
    @app.route("/ventas/data_master/brakers/update{nombre_funcion}", methods=["POST"])
    def update_{nombre_funcion}_route():
        try:
            data = request.get_json()
            item = data.get("item")
            description = data.get("description")
            price = data.get("price")
            type = data.get("type")

            if not all([item, description, price, type]):
                return jsonify({{"error": "Todos los campos son obligatorios."}}), 400

            # Crear diccionario de datos usando el mapeo de campos
            data_dict = {{
                FIELD_MAPPING["id"]: item,
                FIELD_MAPPING["description"]: description,
                FIELD_MAPPING["price"]: price,
                FIELD_MAPPING["type"]: type
            }}
            
            # Llamar a la función de actualización
            update_{nombre_funcion}(data_dict)
            
            return jsonify({{"message": f"{{NombreModulo}} actualizado exitosamente."}}), 200
        except Exception as e:
            return jsonify({{"error": str(e)}}), 500
    
    # /////////////////////////////////
    #   Eliminar Breakers
    # ////////////////////////////////
    @app.route("/ventas/data_master/{nombre_funcion}/delete{nombre_funcion}", methods=["POST"])
    def delete_{nombre_funcion}_route():
        """Elimina un {nombre_funcion} de la base de datos."""
        try:
            data = request.get_json()
            item = data.get("item")
            if not item:
                return jsonify({{"error": "El campo Item es obligatorio."}}), 400
            
            delete_{nombre_funcion}(item)
            return jsonify({{"message": f"{{NombreModulo}} eliminado exitosamente."}}), 200
        except Exception as e:
            return jsonify({{"error": str(e)}}), 500
    
    # /////////////////////////////////
    #   Actualizar fichas técnicas
    # ////////////////////////////////
    @app.route("/ventas/data_master/{nombre_funcion}/actualizar{nombre_funcion}", methods=["GET"])
    def updated_{nombre_funcion}_route():
        """Actualiza todas las Fichas técnicas verificando su existencia en el servidor."""        
        try:
            # Configuramos la ruta al módulo y el nombre de la función  
            module_path = "Consultas_SQL.Ventas.VentasEUA.DataMaster.{ArchivoSQLOptimizado}"
            function_name = "searchitems"
            
            # Llamamos a verificar_actualizar_archivos con la configuración dinámica
            resultado = verificar_actualizar_archivos(
                tabla="{table_name}",
                campo_id=FIELD_MAPPING["id"],
                campo_url=FIELD_MAPPING["technical_sheet"],
                update_function=update_{nombre_funcion}_{technical_sheet},
                ruta_remota=ruta_pdf,
                module_path=module_path,
                search_function_name=function_name,
                base_url="https://file.sycelephant.com",
                carpeta=carpeta
            )
            
            if resultado["exito"]:
                return jsonify({{"message": resultado["mensaje"]}}), 200
            else:
                print(f"Error en actualización: {{resultado['mensaje']}}")
                return jsonify({{"error": resultado["mensaje"]}}), 500
                
        except Exception as e:
            import traceback
            print(f"Error en la actualización: {{str(e)}}")
            print(traceback.format_exc())
            return jsonify({{"error": str(e)}}), 500'''

# Escribir el contenido en el archivo Flask
with open(ruta_archivo_app, 'w', encoding='utf-8') as archivo:
    archivo.write(contenido_app)

print(f"[2/4] Archivo Flask creado exitosamente en: {ruta_archivo_app}")

#==============================================================================
# ARCHIVO 3: Archivo JavaScript Optimizado
#==============================================================================
# Ruta relativa desde la raíz del proyecto para el archivo JavaScript
ruta_relativa_js = os.path.join("src", "static", "js", "Ventas", "VentasEUA", "DataMaster")
ruta_completa_js = os.path.join(ruta_proyecto, ruta_relativa_js)
nombre_archivo_js = f"DM{nombre_funcion}Optimizado.js"
ruta_archivo_js = os.path.join(ruta_completa_js, nombre_archivo_js)

# Crear los directorios si no existen
os.makedirs(ruta_completa_js, exist_ok=True)

# Contenido del archivo JavaScript
contenido_js = f'''// Optimized JavaScript Code
/**
 * DM{nombre_funcion}Optimizado.js - Gestión de catálogo de {nombre_funcion}
 * Ruta: src\\static\\js\\Ventas\\VentasEUA\\DataMaster\\DM{nombre_funcion}Optimizado.js
 */

// Definición de constantes globales
const NOMBRE_MODULO = "{NombreModulo}";

// Mapeo de campos - se actualizará con datos del servidor
let FIELD_MAPPING = {{
    id: "{id_field}",
    description: "{description_field}",
    price: "{price_field}",
    type: "{type_field}",
    technicalSheet: "{technical_sheet}"
}};

function agregarEstilosOverlay() {{
    // Verificar si ya existe el estilo
    if (!document.getElementById('overlay-styles')) {{
        const style = document.createElement('style');
        style.id = 'overlay-styles';
        style.innerHTML = `
            .page-overlay {{
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background-color: rgba(0, 0, 0, 0.5);
                z-index: 9999;
                display: flex;
                justify-content: center;
                align-items: center;
                flex-direction: column;
            }}
            .spinner-container {{
                text-align: center;
                color: white;
            }}
            .spinner-container .spinner-border {{
                width: 3rem;
                height: 3rem;
            }}
            .spinner-text {{
                margin-top: 15px;
                font-size: 18px;
                font-weight: bold;
            }}
        `;
        document.head.appendChild(style);
    }}
}}

function mostrarSpinner(mensaje = "Actualizando fichas técnicas...") {{
    agregarEstilosOverlay();
    
    // Crear el overlay
    const overlay = document.createElement('div');
    overlay.className = 'page-overlay';
    overlay.id = 'actualizacion-overlay';
    
    // Crear el spinner y el mensaje
    overlay.innerHTML = `
        <div class="spinner-container">
            <div class="spinner-border text-light" role="status">
                <span class="visually-hidden">Cargando...</span>
            </div>
            <div class="spinner-text">${{mensaje}}</div>
        </div>
    `;
    
    // Agregar a la página
    document.body.appendChild(overlay);
    
    // Bloquear el scroll
    document.body.style.overflow = 'hidden';
}}

function ocultarSpinner() {{
    const overlay = document.getElementById('actualizacion-overlay');
    if (overlay) {{
        overlay.remove();
        document.body.style.overflow = 'auto';
    }}
}}
// =====================================
// Configuración y Variables Globales
// =====================================
const CONFIG = {{
    API_URL: '/ventas/data_master/{nombre_funcion}/list{nombre_funcion}',
    TABLA_ID: '{nombre_funcion}Table',
    MONEDA: {{
        estilo: 'currency',
        moneda: 'MXN',
        locale: 'es-MX'
    }},
    DATATABLE: {{
        columnDefs: [
            {{ targets: 0, width: "15%", className: 'text-center' }},
            {{ targets: 1, width: "35%", className: 'text-left' }},
            {{ targets: [2,3], width: "15%", className: 'text-center' }},
            {{ targets: 4, width: "5%" }},
            {{ targets: 5, width: "15%" }}
        ]
    }}
}};

// =====================================
// Gestión del Indicador de Carga
// =====================================
const LoadingManager = {{
    element: document.getElementById("loading-indicator"),
    
    show() {{
        if (this.element) this.element.style.display = "block";
    }},
    
    hide() {{
        if (this.element) this.element.style.display = "none";
    }}
}};

// =====================================
//  Limpiar items
// =====================================
function limpiaritems() {{
    document.getElementById("Item").value = "";
    document.getElementById("Description").value = "";
    document.getElementById("Price").value = "";
    document.getElementById("Tipo").value = "";
    document.getElementById("TechnicalSheet").value = "";
}}

// =====================================
// Actualizar Fichas Tecnicas
// =====================================
async function actualizarfichastecnicas() {{
    // Mostrar el spinner antes de iniciar la petición
    mostrarSpinner();
    try {{
        const response = await fetch('/ventas/data_master/{nombre_funcion}/actualizar{nombre_funcion}', {{
            method: 'GET',
            headers: {{
                'Content-Type': 'application/json'
            }}
        }});

        // Agregar esto para depurar
        const responseData = await response.json();
        console.log("Respuesta del servidor:", responseData);
        // Ocultar el spinner una vez que hay respuesta
        ocultarSpinner();
        if (response.ok) {{
            Swal.fire({{
                title: "Actualizado!",
                text: "Los archivos se han actualizado exitosamente.",
                icon: "success"
            }}).then(() => {{
                location.reload(); // Recargar la página para actualizar la tabla
            }});
        }}
    }} catch (error) {{
        // Asegurarse de que el spinner se oculte incluso si hay un error
        ocultarSpinner();
        console.error("Error", error);
        Swal.fire({{
            title: "Error!",
            text: "Ocurrió un error.",
            icon: "error"
        }});
    }}
}}

// =====================================
// Gestión de la Tabla de {nombre_funcion}
// =====================================
class {nombre_funcion} {{
    constructor() {{
        this.table = null;
        this.initializeTable();
    }}

    async initializeTable() {{
        LoadingManager.show();
        try {{
            const responseData = await this.fetchBreakersData();
            
            // Actualizar mapeo de campos si viene del servidor
            if (responseData.fieldMapping) {{
                FIELD_MAPPING = responseData.fieldMapping;
                console.log("Mapeo de campos actualizado desde el servidor:", FIELD_MAPPING);
            }}
            
            // Usar los datos para renderizar
            const data = responseData.data || responseData;
            this.renderTableData(data);
            this.initializeDataTable();
            this.setupEventListeners();
        }} catch (error) {{
            console.error("Error al inicializar la tabla:", error);
        }} finally {{
            LoadingManager.hide();
        }}
    }}

    async fetchBreakersData() {{
        try {{
            const response = await fetch(CONFIG.API_URL);
            const data = await response.json();
            console.log("Datos obtenidos:", data); // Verificar los datos recibidos
            return data;
        }} catch (error) {{
            console.error("Error al obtener los datos:", error);
            return [];
        }}
    }}

    renderTableData(data) {{
        const tableBody = document.getElementById("TableBody");
        if (!tableBody) {{
            console.error("No se encontró el elemento TableBody");
            return;
        }}
        tableBody.innerHTML = data.map(breaker => this.createTableRow(breaker)).join('');
    }}

    createTableRow(breaker) {{
        // Verificación de datos para depuración
        if (!breaker) {{
            console.error("Se recibió un registro nulo o indefinido");
            return "";
        }}
        
        console.log("Procesando registro:", breaker);
        
        // Accede a las propiedades con los nombres dinámicos desde el mapeo
        let longitud = breaker.TechnicalSheet ? breaker.TechnicalSheet.length : 0;
        let icono;
    
        if (longitud > 0) {{
            icono = `<a href="${{breaker.TechnicalSheet}}" target="_blank"><i class="fas fa-file-pdf fa-2x" style="color:red; cursor: pointer;"></i></a>`;
        }} else {{
            icono = '<i class="fas fa-file-pdf-o"></i>';
        }}
        
        return `
            <tr>
                <td>${{breaker[FIELD_MAPPING.id]}}</td>
                <td>${{breaker[FIELD_MAPPING.description]}}</td>
                <td>${{breaker[FIELD_MAPPING.price]}}</td>
                <td>${{breaker[FIELD_MAPPING.type]}}</td>
                <td>${{icono}}</td>
                <td>
                    <div class="btn-group">
                        <button type="button" class="btn btn-primary dm-btn-small editarbreakers" 
                                data-bs-toggle="modal" data-bs-target="#editar{nombre_funcion}Catalog">
                            <i class="fas fa-edit"></i>
                            <span class="dm-btn-text">Editar</span>
                        </button>
                        <button type="button" class="btn btn-danger dm-btn-small ocultar-btn">
                            <i class="fas fa-trash"></i>
                            <span class="dm-btn-text">Eliminar</span>
                        </button>
                    </div>
                </td>
            </tr>
        `;
    }}

    initializeDataTable() {{
        if ($.fn.DataTable.isDataTable("#" + CONFIG.TABLA_ID)) {{
            $('#' + CONFIG.TABLA_ID).DataTable().destroy();
        }}

        this.table = $('#' + CONFIG.TABLA_ID).DataTable({{
            dom: '<"d-flex justify-content-between align-items-center mb-2" Bf>rtip',
            buttons: [{{
                extend: 'excel',
                text: '<i class="fas fa-file-excel"></i> Excel',
                className: 'btn btn-success dm-btn-xs dm-custom-excel-button'
            }}],
            scrollX: true,
            autoWidth: true,
            ordering: false,
            fixedHeader: true,
            searching: true,
            columnDefs: [
                {{ targets: 0, width: "15%" }},
                {{ targets: 1, width: "40%" }},
                {{ targets: 2, width: "15%" }},
                {{ targets: 3, width: "30%" }},
                {{
                    targets: 0,
                    className: 'text-center',
                }},
                {{
                    targets: 1,
                    className: 'text-left',
                }},
                {{
                    targets: 2,
                    className: 'text-center',
                    render: function(data, type, row) {{
                        if (type === 'display' || type === 'filter') {{
                            // Verificar si el dato es un número válido
                            const number = parseFloat(data);
                            if (!isNaN(number)) {{
                                return new Intl.NumberFormat('es-MX', {{
                                    style: 'currency',
                                    currency: 'MXN'
                                }}).format(number);
                            }} else {{
                                return data; // Devolver el dato original si no es un número válido
                            }}
                        }}
                        return data;
                    }}
                }},
                {{
                    targets: 3,
                    className: 'text-center',
                }},
                {{
                    targets: 4,
                    className: 'text-center',
                }},
            ],
            language: {{
                search: "Buscar:",
                lengthMenu: "Mostrar _MENU_ registros por página",
                info: "Mostrando _START_ a _END_ de _TOTAL_ registros",
                infoEmpty: "Mostrando 0 a 0 de 0 registros",
                paginate: {{
                    first: "Primero",
                    last: "Último",
                    next: "Siguiente",
                    previous: "Anterior"
                }}
            }}
        }});
    }}

    setupEventListeners() {{
        // Filtros de columna
        $('.column-filter').on('keyup change', (e) => {{
            const columnIndex = $(e.target).data('column');
            this.table.column(columnIndex).search(e.target.value).draw();
        }});

        // Edición de registros
        $('#' + CONFIG.TABLA_ID).on('click', '.editarbreakers', this.handleEdit);

        // Eliminación de registros
        $('#TableBody').on('click', '.ocultar-btn', this.handleDelete.bind(this));
    }}

    handleEdit(e) {{
        const row = e.target.closest('tr');
        if (!row) return;

        const [item, description, price, tipo] = Array.from(row.cells)
            .map((cell, index) => {{
                const value = cell.textContent.trim();
                return index === 2 ? value.replace(/[^\\d.-]/g, '') : value;
            }});

        document.getElementById("ItemEdit").value = item;
        document.getElementById("DescriptionEdit").value = description;
        document.getElementById("PriceEdit").value = price;
        document.getElementById("TipoEdit").value = tipo;
    }}

    async handleDelete(e) {{
        const row = $(e.target).closest('tr');
        const item = row.find('td').eq(0).text().trim();
        
        const result = await Swal.fire({{
            title: "¿Estás seguro de eliminar este item?",
            icon: "warning",
            showCancelButton: true,
            confirmButtonColor: "#3085d6",
            cancelButtonColor: "#d33",
            confirmButtonText: "Sí, eliminar!"
        }});

        if (result.isConfirmed) {{
            try {{
                const response = await fetch('/ventas/data_master/{nombre_funcion}/delete{nombre_funcion}', {{
                    method: 'POST',
                    headers: {{
                        'Content-Type': 'application/json'
                    }},
                    body: JSON.stringify({{ item: item }})
                }});

                if (response.ok) {{
                    row.fadeOut(500, () => {{ 
                        this.table.row(row).remove().draw();
                        Swal.fire({{
                            title: "Eliminado!",
                            text: "El item ha sido eliminado.",
                            icon: "success"
                        }});
                    }});
                }} else {{
                    const errorData = await response.json();
                    Swal.fire({{
                        title: "Error!",
                        text: errorData.error,
                        icon: "error"
                    }});
                }}
            }} catch (error) {{
                console.error(`Error al eliminar el ${{NOMBRE_MODULO}}:`, error);
                Swal.fire({{
                    title: "Error!",
                    text: `Ocurrió un error al eliminar el ${{NOMBRE_MODULO}}.`,
                    icon: "error"
                }});
            }}
        }}
    }}
}}

// =====================================
// Gestión de Modales
// =====================================
class ModalManager {{
    static cerrarModalEditar() {{
        const modal = document.getElementById("editar{nombre_funcion}Catalog");
        const modalInstance = bootstrap.Modal.getInstance(modal);
        
        if (modalInstance) {{
            modalInstance.hide();
        }}

        setTimeout(() => {{
            document.querySelectorAll(".modal-backdrop").forEach(backdrop => backdrop.remove());
            document.body.classList.remove("modal-open");
            document.body.style.overflow = "auto";
        }}, 300);
    }}

    static validarFormulario(event) {{
        const campos = [
            {{ id: "Item", mensaje: "El campo Item es obligatorio." }},
            {{ id: "Description", mensaje: "El campo Descripción es obligatorio." }},
            {{ id: "Price", mensaje: "El campo Precio es obligatorio." }},
            {{ id: "Tipo", mensaje: "Debes seleccionar un tipo.", invalidValue: "Elige una opción" }}
        ];

        let formularioValido = true;

        campos.forEach(campo => {{
            const input = document.getElementById(campo.id);
            const valor = input.value.trim();
            let mensajeError = document.getElementById(`${{campo.id}}-error`);

            if (!mensajeError) {{
                mensajeError = document.createElement("small");
                mensajeError.id = `${{campo.id}}-error`;
                mensajeError.style.color = "red";
                input.parentNode.appendChild(mensajeError);
            }}

            if (!valor || valor === campo.invalidValue) {{
                mensajeError.textContent = campo.mensaje;
                formularioValido = false;
            }} else {{
                mensajeError.textContent = "";
            }}
        }});

        if (!formularioValido) {{
            event.preventDefault();
        }}
    }}
}}

// =====================================
// Cerrar modal editar (función global para acceder desde el HTML)
// =====================================
function cerrarModalEditar() {{
    ModalManager.cerrarModalEditar();
}}

// =====================================
// Inicialización cuando el DOM está listo
// =====================================
document.addEventListener("DOMContentLoaded", () => {{
    // Inicializar la tabla
    const breakersTableInstance = new {nombre_funcion}();
    
    // Agregar event listener para el botón de eliminar
    document.getElementById("TableBody").addEventListener("click", function(e) {{
        if (e.target.classList.contains("ocultar-btn") || e.target.closest(".ocultar-btn")) {{
            breakersTableInstance.handleDelete(e);
        }}
    }});
    
    // Agregar listener para el formulario de agregar
    const formAgregar{nombre_funcion} = document.getElementById("formAgregar{nombre_funcion}");
    if (formAgregar{nombre_funcion}) {{
        formAgregar{nombre_funcion}.addEventListener("submit", async function(event) {{
            event.preventDefault();
           
            const item = document.getElementById("Item").value.trim();
            const description = document.getElementById("Description").value.trim();
            const price = document.getElementById("Price").value.trim();
            const tipo = document.getElementById("Tipo").value.trim();
            const technicalSheet = document.getElementById("TechnicalSheet").files;
            const formData = new FormData();
            
            formData.append('item', item);
            formData.append('description', description);
            formData.append('price', price);
            formData.append('type', tipo);

            // Validar y añadir el archivo PDF si existe
            if (technicalSheet.length > 0) {{
                if (!technicalSheet[0].name.toLowerCase().endsWith('.pdf')) {{
                    Swal.fire({{
                        title: "Error!",
                        text: "Por favor selecciona un archivo PDF.",
                        icon: "error"
                    }});
                    return;
                }}
                formData.append('TechnicalSheet', technicalSheet[0]);
            }}
            
            // Mostrar el spinner antes de iniciar la petición
            mostrarSpinner();
            
            try {{
                const response = await fetch('/ventas/data_master/brakers/add{nombre_funcion}', {{
                    method: 'POST',
                    body: formData
                }});
                
                // Ocultar el spinner una vez que hay respuesta
                ocultarSpinner();
                
                if (response.ok) {{
                    Swal.fire({{
                        title: "Guardado!",
                        text: `El ${{NOMBRE_MODULO}} ha sido guardado exitosamente.`,
                        icon: "success"
                    }}).then(() => {{
                        // Limpiar todas las cajas de texto
                        limpiaritems();
                        location.reload(); // Recargar la página para actualizar la tabla
                    }});
                }} else {{
                    const errorData = await response.json();
                    Swal.fire({{
                        title: "Error!",
                        text: errorData.error,
                        icon: "error"
                    }});
                }}
            }} catch (error) {{
                // Ocultar el spinner una vez que hay respuesta
                ocultarSpinner();
                console.error(`Error al guardar el ${{NOMBRE_MODULO}}:`, error);
                Swal.fire({{
                    title: "Error!",
                    text: `Ocurrió un error al guardar el ${{NOMBRE_MODULO}}.`,
                    icon: "error"
                }});
            }}
        }});
    }}

    // Agregar listener para el formulario de editar
    const formEditar{nombre_funcion} = document.getElementById("formEditar{nombre_funcion}");
    if (formEditar{nombre_funcion}) {{
        formEditar{nombre_funcion}.addEventListener("submit", async function(event) {{
            event.preventDefault();

            const item = document.getElementById("ItemEdit").value.trim();
            const description = document.getElementById("DescriptionEdit").value.trim();
            const price = document.getElementById("PriceEdit").value.trim();
            const tipo = document.getElementById("TipoEdit").value.trim();

            const data = {{
                item: item,
                description: description,
                price: price,
                type: tipo
            }};

            try {{
                const response = await fetch('/ventas/data_master/brakers/update{nombre_funcion}', {{
                    method: 'POST',
                    headers: {{
                        'Content-Type': 'application/json'
                    }},
                    body: JSON.stringify(data)
                }});

                if (response.ok) {{
                    Swal.fire({{
                        title: "Actualizado!",
                        text: `El ${{NOMBRE_MODULO}} ha sido actualizado exitosamente.`,
                        icon: "success"
                    }}).then(() => {{
                        location.reload(); // Recargar la página para actualizar la tabla
                    }});
                }} else {{
                    const errorData = await response.json();
                    Swal.fire({{
                        title: "Error!",
                        text: errorData.error,
                        icon: "error"
                    }});
                }}
            }} catch (error) {{
                console.error(`Error al actualizar el ${{NOMBRE_MODULO}}:`, error);
                Swal.fire({{
                    title: "Error!",
                    text: `Ocurrió un error al actualizar el ${{NOMBRE_MODULO}}.`,
                    icon: "error"
                }});
            }}
        }});
    }}

    // Corregir el atributo form en el botón de actualizar
    const formEditarBtn = document.querySelector("button[form='formEditarbreakers']");
    if (formEditarBtn) {{
        formEditarBtn.setAttribute("form", "formEditar{nombre_funcion}");
    }}

    // Limpieza de modal
    const modalElement = document.getElementById("editar{nombre_funcion}Catalog");
    if (modalElement) {{
        modalElement.addEventListener("hidden.bs.modal", ModalManager.cerrarModalEditar);
    }}
}});'''

# Escribir el contenido en el archivo JavaScript
with open(ruta_archivo_js, 'w', encoding='utf-8') as archivo:
    archivo.write(contenido_js)

print(f"[3/4] Archivo JavaScript creado exitosamente en: {ruta_archivo_js}")

#==============================================================================
# ARCHIVO 4: Archivo HTML
#==============================================================================
# Ruta relativa desde la raíz del proyecto para el archivo HTML
ruta_relativa_html = os.path.join("src", "templates", "Ventas", "VentasEUA", "DataMaster")
ruta_completa_html = os.path.join(ruta_proyecto, ruta_relativa_html)
nombre_archivo_html = f"DM{nombre_funcion}Optimizado.html"
ruta_archivo_html = os.path.join(ruta_completa_html, nombre_archivo_html)

# Crear los directorios si no existen
os.makedirs(ruta_completa_html, exist_ok=True)

# Contenido del archivo HTML
contenido_html = f'''
{{% extends "layout.html" %}}
{{% block title %}}
{NombreModulo} - Data Master
{{% endblock %}}

{{% block content %}}
<link rel="stylesheet" href="{{ url_for('static', filename='css/DatamastersGral.css') }}">
<!-- css/Ventas/VentasEUA/datamaster/DMBreakers.css -->

<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/2.4.2/css/buttons.dataTables.min.css" />

<main class="page">
  <section class="clean-block clean-form dark">
    <div class="container">
      <div class="block-heading">
        <h2 class="text-info ">{NombreModulo}</h2>
        <p>Administre los tipos de protección de interruptores registrados en el sistema.</p>
      </div>

      <!-- Indicador de carga -->
      <div id="loading-indicator" style="display: none; text-align: center; margin-top: 10px;">
        <span class="spinner-border text-primary" role="status" aria-hidden="true"></span>
        <span>Cargando...</span>
      </div>
      <!-- Controles (Regresar y Agregar) -->
      <div class="d-flex justify-content-between mb-3">
          <!-- Botón Regresar -->
          <button 
              type="button" 
              class="btn btn-secondary dm-btn-Secundario" 
              onclick="history.back();">
              <i class="fas fa-arrow-left"></i> 
              Regresar
          </button>

         
      
          <!-- Contenedor para los botones Actualizar y Agregar -->
          <div>
              <!-- Botón Actualizar -->
              <button 
                  type="button" 
                  class="btn btn-success dm-btn-Guardar" onclick="actualizarfichastecnicas();">
                  Actualizar <i class="fas fa-sync-alt"></i>
              </button>
      
              <!-- Botón Agregar (abre modal) -->
              <button 
                  type="button" 
                  class="btn btn-success dm-btn-Guardar" 
                  data-bs-toggle="modal" 
                  data-bs-target="#agregar{nombre_funcion}Catalog">
                  Agregar <i class="fas fa-plus"></i>
              </button>
          </div>
      </div>
      
      <!-- Tabla principal -->
      <table class="table table-striped dm-table" style="width:100%" id="{nombre_funcion}Table">
        <thead>
          <tr>
            <th class="dm-table">Item</th>
            <th class="dm-table">Descripción</th>
            <th class="dm-table">Precio</th>
            <th class="dm-table">Tipo</th>
            <th class="dm-table">Ficha Tecnica</th>
            <th class="dm-table">Acciones</th>
          </tr>
          <tr class="dm-btn-text">
            <th class="dm-table"><input type="text" class="form-control column-filter" data-column="0" placeholder="Buscar Item"></th>
            <th class="dm-table"><input type="text" class="form-control column-filter" data-column="1" placeholder="Buscar Descripción"></th>
            <th class="dm-table"><input type="text" class="form-control column-filter" data-column="2" placeholder="Buscar Precio"></th>
            <th class="dm-table"><input type="text" class="form-control column-filter" data-column="3" placeholder="Buscar Tipo"></th>
            <th></th> 
            <th></th>  
          </tr>
        </thead>
        <tbody id="TableBody">
        </tbody>
      </table>

      <!-- Modal Editar -->
      <div 
        class="modal fade dm-custom-modal" 
        id="editar{nombre_funcion}Catalog" 
        tabindex="-1"
        aria-labelledby="editar{nombre_funcion}CatalogLabel" 
        aria-hidden="true">
        <div class="modal-dialog">
          <div class="modal-content">
            <!-- Encabezado del modal -->
            <div class="modal-header">
              <h1 class="modal-title fs-5" id="editar{nombre_funcion}CatalogLabel">
                Editar Elementos
              </h1>
              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="cerrarModalEditar();"></button>
            </div>
            <!-- Cuerpo del modal -->
            <div class="dm-modal-body">
              <form id="formEditar{nombre_funcion}" class="p-3">
                <div class="row">
                  <div class="col-md-4">
                    <div class="form-group">
                      <label for="ItemEdit" class="form-label">Item</label>
                      <input type="text" class="form-control dm-input-style" id="ItemEdit" disabled />
                    </div>
                  </div>
                </div>

                <div class="form-group">
                  <label for="DescriptionEdit" class="form-label">Description</label>
                  <textarea class="form-control dm-input-style" id="DescriptionEdit"></textarea>
                </div>

                <div class="row">
                  <div class="col-md-5">
                    <div class="form-group">
                      <label for="PriceEdit" class="form-label">Price</label>
                      <input type="number" class="form-control dm-input-style" step="0.01" id="PriceEdit"/>
                    </div>
                  </div>
                  <div class="col-md-7">
                    <div class="form-group">
                      <label for="TipoEdit" class="form-label">Tipo</label>
                      <select class="form-select dm-input-style" id="TipoEdit">
                        <option value="">Elige una opción</option>
                        <option value="THERMOMAGNETIC">THERMOMAGNETIC</option>
                        <option value="ELECTROMAGNETIC">ELECTROMAGNETIC</option>
                      </select>
                      <br>
                    </div>
                  </div>
                </div>
              </form>
            </div>
            <!-- Footer del modal -->
            <div class="modal-footer">
              <button type="button" class="btn btn-secondary dm-btn-Secundario" data-bs-dismiss="modal" onclick="cerrarModalEditar();">
                Cerrar <i class="fas fa-times"></i>
              </button>
              <button type="submit" class="btn btn-success dm-btn-Guardar" form="formEditar{nombre_funcion}">
                Actualizar <i class="fas fa-sync-alt"></i>
              </button>
            </div>
          </div>
        </div>
      </div>

      <!-- Modal Agregar -->
      <div 
        class="modal fade dm-custom-modal" 
        id="agregar{nombre_funcion}Catalog" 
        tabindex="-1" 
        aria-labelledby="agregar{nombre_funcion}CatalogLabel"
        aria-hidden="true">
        <div class="modal-dialog">
          <div class="modal-content">
            <!-- Encabezado del modal -->
            <div class="modal-header">
              <h1 class="modal-title fs-5" id="agregar{nombre_funcion}CatalogLabel">
                Agregar Elementos
              </h1>
              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="limpiaritems();"></button>
            </div>
            <!-- Cuerpo del modal -->
           
            <div class="dm-modal-body">
              <form id="formAgregar{nombre_funcion}" class="p-3" enctype="multipart/form-data">
                <div>
                  <label for="Item" class="form-label">Item</label>
                  <input type="text" class="form-control dm-input-style" id="Item" required/>
                </div>
                <div>
                  <label for="Description" class="form-label">
                    Descripción
                  </label>
                  <textarea class="form-control dm-input-style" id="Description" required></textarea>
                </div>
                <div class="row">
                  <div class="col-md-5 mb-1">
                    <label for="Price" class="form-label">Precio</label>
                    <input type="number" class="form-control dm-input-style" step="0.01" id="Price" required />
                  </div>

                  <div class="col-md-7 mb-1">
                    <label for="Tipo" class="form-label">Tipo</label>
                    <select id="Tipo" class="form-select dm-input-style" aria-label="Default select example" required>
                      <option value="">Elige una opción</option>
                      <option value="THERMOMAGNETIC">THERMOMAGNETIC</option>
                      <option value="ELECTROMAGNETIC">ELECTROMAGNETIC</option>
                    </select>
                  </div>
                </div>

                <div class="row">
                  <div class="col-md-12 mb-1">
                    <label for="TechnicalSheet" class="form-label">Ficha Tecnica</label>
                    <input type="file" class="form-control dm-input-style" id="TechnicalSheet" accept=".pdf" />
                  </div>
                </div>
            </div>
            <!-- Footer del modal -->
            <div class="modal-footer">
              <button type="button" class="btn btn-secondary dm-btn-Secundario" data-bs-dismiss="modal" onclick="limpiaritems();">
                Cerrar <i class="fas fa-times"></i>
              </button>
              <button type="submit" class="btn btn-success dm-btn-Guardar">
                Guardar <i class="fas fa-save"></i>
              </button>
            </form>
            </div>
          </div>
        </div>
      </div>

    </div> <!-- Fin container -->
  </section>
</main>

<!-- Scripts de jQuery y DataTables -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.flash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.html5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.print.min.js"></script>

<!-- Script principal de la página -->
<script src="{{ url_for('static', filename='js/Ventas/VentasEUA/DataMaster/DM{nombre_funcion}Optimizado.js') }}"></script>
{{% endblock %}}'''

# Escribir el contenido en el archivo HTML
with open(ruta_archivo_html, 'w', encoding='utf-8') as archivo:
    archivo.write(contenido_html)

print(f"[4/4] Archivo HTML creado exitosamente en: {ruta_archivo_html}")
print("\n=== Resumen de la generación ===")
print(f"1. SQL:  {os.path.basename(ruta_archivo_sql)}")
print(f"2. Flask: {os.path.basename(ruta_archivo_app)}")
print(f"3. JS:   {os.path.basename(ruta_archivo_js)}")
print(f"4. HTML: {os.path.basename(ruta_archivo_html)}")
print("==============================")
print(f"Módulo {NombreModulo} generado con éxito!")