import os
from flask import Flask, jsonify, request, render_template, session, redirect, url_for, flash
from flask_cors import CORS
from flask_mail import Mail
from datetime import datetime, timedelta
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import text
import logging
from extensions import db
import traceback
from flask_socketio import SocketIO, emit
from flask_login import LoginManager, login_user, login_required, logout_user, current_user
from functools import wraps
from werkzeug.security import check_password_hash
from flask_mail import Message
import random
import string
import uuid
from logging.handlers import RotatingFileHandler
from sqlalchemy.orm import joinedload
from apscheduler.schedulers.background import BackgroundScheduler

# Configuro el logging para mi aplicación
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Inicializo mi aplicación Flask
app = Flask(__name__)
socketio = SocketIO(app)
app.secret_key = os.environ.get('FLASK_SECRET_KEY') or '8f42a73d4f8c4c1d9c525f68995ced6b2537a quaracer6492'

# Configuro CORS para mi aplicación
CORS(app, resources={r"/api/*": {
    "origins": ["http://194.163.45.32"],
    "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    "allow_headers": ["Content-Type", "Authorization"],
    "supports_credentials": True
}})

# Configuro la conexión a mi base de datos
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:1234@194.163.45.32/inflables_medellin'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Inicializo la base de datos
db.init_app(app)

# Probar la conexión a la base de datos
@app.route('/test-db-connection')
def test_db_connection():
    try:
        # Ejecutar una consulta simple
        result = db.session.execute(text('SELECT 1'))
        db.session.commit()  # Confirmar la transacción (si es necesario)
        return "Conexión a la base de datos exitosa.", 200
    except SQLAlchemyError as e:
        return f"Error al conectar a la base de datos: {str(e)}", 500

# Importo mis modelos después de inicializar db
from models import OrdenesCompra1, OrdenesCompra2, Entradas1, Entradas2, Salidas1, Salidas2, Referencias, Unidades, Usuarios, Consecutivos, Grupo, Licencia, Clientes

# Configuro el servidor de correo para mi aplicación
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False
app.config['MAIL_USERNAME'] = 'info@migsistemas.com'
app.config['MAIL_PASSWORD'] = 'eejgelzxmgrfrkdh'
app.config['MAIL_DEFAULT_SENDER'] = 'info@migsistemas.com'
mail = Mail(app)

# Verifico la conexión a la base de datos y creo las tablas si no existen
with app.app_context():
    try:
        db.session.execute(text('SELECT 1'))
        logger.info("Conexión a la base de datos exitosa")
        db.create_all()
        logger.info("Tablas creadas exitosamente")
    except SQLAlchemyError as e:
        logger.error(f"Error al conectar a la base de datos o crear tablas: {str(e)}")

#-----------------------------------------------------------------------------------------#

# Configuración de LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

@login_manager.user_loader
def load_user(user_id):
    return Usuarios.query.get(user_id)

@app.route('/')
@login_required
def index():
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))
    return redirect(url_for('login'))

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        logger.info(f"Usuario ya autenticado: {current_user.IdUsuario}")
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        logger.info("Intento de login")
        try:
            data = request.json
            username = data.get('username')
            password = data.get('password')
            logger.debug(f"Intento de login para usuario: {username}")
            
            user = Usuarios.query.filter_by(IdUsuario=username).first()
            if user:
                logger.debug(f"Usuario encontrado: {user}")
                logger.debug(f"Estado activo del usuario: {user.is_active}")
                if user.Password == password:
                    if user.is_active:
                        logger.info(f"Login exitoso para usuario: {username}")
                        login_user(user)
                        user.UltimoAcceso = db.func.now()
                        db.session.commit()
                        return jsonify({"success": True, "message": "Login exitoso"})
                    else:
                        logger.warning(f"Intento de login para usuario inactivo: {username}")
                        return jsonify({"success": False, "message": "Usuario inactivo"}), 401
                else:
                    logger.warning(f"Contraseña incorrecta para usuario: {username}")
                    return jsonify({"success": False, "message": "Usuario o contraseña incorrectos"}), 401
            else:
                logger.warning(f"Usuario no encontrado: {username}")
                return jsonify({"success": False, "message": "Usuario o contraseña incorrectos"}), 401
        except AttributeError as e:
            logger.error(f"Error de atributo en el proceso de login: {str(e)}", exc_info=True)
            return jsonify({"success": False, "message": f"Error en la estructura del usuario: {str(e)}"}), 500
        except Exception as e:
            logger.error(f"Error en el proceso de login: {str(e)}", exc_info=True)
            return jsonify({"success": False, "message": f"Error interno del servidor: {str(e)}"}), 500
    
    logger.info("Renderizando página de login")
    return render_template('login.html')

@app.route('/verificar_usuario/<nombre_usuario>')
def verificar_usuario(nombre_usuario):
    usuario = Usuarios.query.filter_by(IdUsuario=nombre_usuario).first()
    if usuario:
        return jsonify({"existe": True, "email": usuario.Email}), 200
    return jsonify({"existe": False}), 404

@app.route('/listar_usuarios')
def listar_usuarios():
    usuarios = Usuarios.query.all()
    return jsonify([{
        "IdUsuario": u.IdUsuario,
        "Nombre": u.Nombre,
        "Email": u.Email,
        "Rol": u.Rol
    } for u in usuarios]), 200

@app.before_request
def log_request_info():
    logger.debug('Headers: %s', request.headers)
    logger.debug('Body: %s', request.get_data())

@app.after_request
def log_response_info(response):
    logger.debug('Response: %s', response.get_data())
    return response

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

@app.route('/dashboard')
@login_required
def dashboard():
    return render_template('index.html', user=current_user)

def generar_codigo_verificacion():
    return ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))

@app.route('/restContrasena', methods=['GET', 'POST'])
def restContrasena():
    if request.method == 'POST':
        data = request.json
        id_usuario = data.get('idUsuario')
        nueva_contrasena = data.get('contrasena')
        
        usuario = Usuarios.query.filter_by(IdUsuario=id_usuario).first()
        if usuario:
            usuario.Password = nueva_contrasena
            db.session.commit()
            logger.info(f"Contraseña restablecida para usuario: {id_usuario}")
            return jsonify({"success": True, "message": "Contraseña restablecida exitosamente"})
        else:
            logger.warning(f"Intento de restablecer contraseña para usuario no existente: {id_usuario}")
            return jsonify({"success": False, "message": "No se encontró ningún usuario con ese ID"})

    return render_template('restContrasena.html')

@app.route('/enviarCodigoVerificacion', methods=['POST'])
def enviar_codigo_verificacion():
    data = request.json
    id_usuario = data.get('idUsuario')
    
    usuario = Usuarios.query.filter_by(IdUsuario=id_usuario).first()
    if not usuario:
        logger.warning(f"Intento de enviar código de verificación a usuario no existente: {id_usuario}")
        return jsonify({"success": False, "message": "Usuario no encontrado"}), 404
    
    codigo_verificacion = generar_codigo_verificacion()
    
    usuario.codigo_verificacion = codigo_verificacion
    db.session.commit()
    
    try:
        msg = Message("Código de Verificación para Restablecer Contraseña",
                      sender="info@migsistemas.com",
                      recipients=[usuario.Email])
        msg.body = f"Tu código de verificación es: {codigo_verificacion}"
        mail.send(msg)
        logger.info(f"Código de verificación enviado a: {usuario.Email}")
        return jsonify({"success": True, "message": "Código de verificación enviado"}), 200
    except Exception as e:
        logger.error(f"Error al enviar correo: {str(e)}")
        return jsonify({"success": False, "message": "Error al enviar el correo"}), 500

@app.route('/verificarCodigoYCambiarContrasena', methods=['POST'])
def verificar_codigo_y_cambiar_contrasena():
    data = request.json
    id_usuario = data.get('idUsuario')
    contrasena = data.get('contrasena')
    codigo_verificacion = data.get('codigoVerificacion')
    
    usuario = Usuarios.query.filter_by(IdUsuario=id_usuario).first()
    if not usuario:
        logger.warning(f"Intento de verificar código para usuario no existente: {id_usuario}")
        return jsonify({"success": False, "message": "Usuario no encontrado"}), 404
    
    if usuario.codigo_verificacion != codigo_verificacion:
        logger.warning(f"Código de verificación incorrecto para usuario: {id_usuario}")
        return jsonify({"success": False, "message": "Código de verificación incorrecto"}), 400
    
    usuario.Password = contrasena
    usuario.codigo_verificacion = None
    db.session.commit()
    logger.info(f"Contraseña cambiada exitosamente para usuario: {id_usuario}")
    
    return jsonify({"success": True, "message": "Contraseña cambiada exitosamente"}), 200

# APARTADO PARA PEDIR LICENCIA
@app.route('/registro')
def registro():
    return render_template('pideLicencia.html')

def generar_codigo_licencia(nit):
    caracteres = string.ascii_uppercase + string.digits
    codigo_aleatorio = ''.join(random.choices(caracteres, k=20))
    return f"{nit[:5]}-{codigo_aleatorio}"

@app.route('/solicitar_licencia', methods=['POST'])
def solicitar_licencia():
    logger.info("Iniciando solicitud de licencia")
    try:
        data = request.form
        nit = data['nit']
        razon_social = data['razonsocial']
        nombre_comercial = data['nombrecomercial']
        email = data['email']
        tipo_licencia = data['tipolicencia']
        fecha_vencimiento = data.get('fechavencimiento')

        codigo_licencia = generar_codigo_licencia(nit)
        username = f"{razon_social[:5].lower()}{random.randint(100, 999)}"
        password = ''.join(random.choices(string.ascii_letters + string.digits, k=10))

        nuevo_usuario = Usuarios(
            IdUsuario=username,
            Nombre=razon_social,
            Apellido=nombre_comercial,
            Email=email,
            Password=password,
            Rol='administrador',
            Telefono=data['telefono'],
            Direccion=data['ubicacioncomercial']
        )

        nueva_licencia = Licencia(
            id=str(uuid.uuid4()),
            razonsocial=razon_social,
            nombrecomercial=nombre_comercial,
            ubicacioncomercial=data['ubicacioncomercial'],
            ciudad=data['ciudad'],
            telefono=data['telefono'],
            version=data['version'],
            numerolicencia=codigo_licencia,
            cantidadusuario=int(data['cantidadusuario']),
            nit=nit,
            tipolicencia=tipo_licencia,
            fechavencimiento=datetime.strptime(fecha_vencimiento, '%Y-%m-%d') if fecha_vencimiento else None
        )

        db.session.add(nuevo_usuario)
        db.session.add(nueva_licencia)
        db.session.commit()
        logger.info(f"Nuevo usuario y licencia creados: {username}")

        msg = Message("Nueva Licencia Generada",
                      sender="info@migsistemas.com",
                      recipients=["riosjuan3053@gmail.com", email])
        
        msg.body = f"""
Se ha generado una nueva licencia:

NIT: {nit}
Razón Social: {razon_social}
Nombre Comercial: {nombre_comercial}
Número de Licencia: {codigo_licencia}
Tipo de Licencia: {tipo_licencia}
Fecha de Vencimiento: {fecha_vencimiento or 'N/A'}

Información de acceso:
Usuario: {username}
Contraseña: {password}

Por favor, cambie su contraseña después del primer inicio de sesión.
        """
        
        mail.send(msg)
        logger.info(f"Correo enviado a: {email}")

        return jsonify({"success": True, "message": "Licencia generada y correo enviado exitosamente."})
    except Exception as e:
        logger.error(f"Error al solicitar licencia: {str(e)}")
        db.session.rollback()
        return jsonify({"success": False, "message": str(e)}), 500

@app.route('/verificar_licencia', methods=['POST'])
def verificar_licencia():
    try:
        data = request.json
        licencia = data['licencia']
        
        licencia_db = Licencia.query.filter_by(numerolicencia=licencia).first()
        
        if licencia_db:
            logger.info(f"Licencia verificada correctamente: {licencia}")
            return jsonify({"success": True, "message": "Licencia verificada correctamente."})
        else:
            logger.warning(f"Intento de verificar licencia inválida: {licencia}")
            return jsonify({"success": False, "message": "Licencia inválida."})
    except Exception as e:
        logger.error(f"Error al verificar licencia: {str(e)}")
        return jsonify({"success": False, "message": str(e)}), 500

@app.route('/registro_completo', methods=['GET', 'POST'])
def registro_completo():
    if request.method == 'POST':
        nuevo_usuario = Usuarios(
            IdUsuario=request.form['id_usuario'],
            Nombre=request.form['nombre'],
            Apellido=request.form['apellido'],
            Email=request.form['email'],
            Password=request.form['password'],
            Rol=request.form['rol'],
            Telefono=request.form['telefono'],
            Direccion=request.form['direccion']
        )
        
        try:
            db.session.add(nuevo_usuario)
            db.session.commit()
            logger.info(f"Nuevo usuario registrado: {nuevo_usuario.IdUsuario}")
            flash('Usuario registrado exitosamente. Por favor, inicia sesión.', 'success')
            return redirect(url_for('login'))
        except Exception as e:
            logger.error(f"Error al registrar usuario: {str(e)}")
            db.session.rollback()
            flash(f'Error al registrar usuario: {str(e)}', 'error')

    return render_template('registro_completo.html')

# APARTADO PARA LA CREACION DE USUARIO
@app.route('/crear_usuario', methods=['GET', 'POST'])
@login_required
def crear_usuario():
    if current_user.Rol != 'administrador':
        flash('No tienes permiso para acceder a esta página.', 'error')
        return redirect(url_for('dashboard'))

    if request.method == 'POST':
        nuevo_usuario = Usuarios(
            IdUsuario=request.form['id_usuario'],
            Nombre=request.form['nombre'],
            Apellido=request.form['apellido'],
            Email=request.form['email'],
            Password=request.form['password'],
            Rol=request.form['rol'],
            Activo=request.form['activo'] == 'true',
            Telefono=request.form['telefono'],
            Direccion=request.form['direccion']
        )
        
        try:
            db.session.add(nuevo_usuario)
            db.session.commit()
            flash('Usuario creado exitosamente.', 'success')
            return redirect(url_for('dashboard'))
        except Exception as e:
            db.session.rollback()
            flash(f'Error al crear usuario: {str(e)}', 'error')

    return render_template('crear_usuario.html')

@app.route('/get_user_role')
@login_required
def get_user_role():
    return jsonify({'role': current_user.Rol})

@app.route('/ordenes-compra')
@login_required
def ordenes_compra():
    return render_template('ordenes_compra.html')

@app.route('/gestion-ordenes')
@login_required
def gestion_ordenes():
    return render_template('gestionar_ordenes_compra.html')

@app.route('/detalle-ordenes')
@login_required
def detalle_ordenes():
    return render_template('detalles_orden_entrega.html')

#-----------------------------------------------------------------------------------------#

# Rutas y funciones para Entradas
@app.route('/entradas', methods=['GET'])
def listar_entradas():
    try:
        entradas = Entradas1.query.all()
        return jsonify([{
            'numero': e.Numero,
            'mes': e.Mes,
            'id_bodega': e.IdBodega,
            'fecha_creacion': e.FechaCreacion.isoformat(),
            'id_usuario': e.IdUsuario,
            'recibe': e.Recibe,
            'id_proyecto': e.IdProyecto,
            'fecha': e.fecha.isoformat() if e.fecha else None,
            'id_cliente': e.idcliente,
            'total': float(e.total)
        } for e in entradas])
    except SQLAlchemyError as e:
        logger.error(f"Error al listar entradas: {str(e)}")
        return jsonify({'error': str(e)}), 400

@app.route('/entradas', methods=['POST'])
def crear_entrada():
    try:
        data = request.json
        nueva_entrada = Entradas1(
            Numero=data['numero'],
            Mes=data['mes'],
            IdBodega=data['id_bodega'],
            FechaCreacion=data['fecha_creacion'],
            IdUsuario=data['id_usuario'],
            CuentaDebito=data.get('cuenta_debito'),
            CuentaCredito=data.get('cuenta_credito'),
            Observaciones=data.get('observaciones'),
            Recibe=data.get('recibe'),
            IdProyecto=data.get('id_proyecto'),
            op=data.get('op'),
            fecha=data.get('fecha'),
            idcliente=data.get('id_cliente')
        )
        db.session.add(nueva_entrada)
        db.session.commit()
        return jsonify({'mensaje': 'Entrada creada con éxito', 'numero': nueva_entrada.Numero}), 201
    except SQLAlchemyError as e:
        db.session.rollback()
        logger.error(f"Error al crear entrada: {str(e)}")
        return jsonify({'error': str(e)}), 400

#-----------------------------------------------------------------------------------------#

# Rutas y funciones para Salidas
@app.route('/salidas', methods=['GET'])
def listar_salidas():
    try:
        salidas = Salidas1.query.all()
        return jsonify([{
            'numero': s.Numero,
            'mes': s.Mes,
            'id_bodega': s.IdBodega,
            'fecha_creacion': s.FechaCreacion.isoformat(),
            'id_usuario': s.IdUsuario,
            'recibe': s.Recibe,
            'id_proyecto': s.idproyecto,
            'fecha': s.fecha.isoformat() if s.fecha else None,
            'num_traslado': s.numtraslado,
            'total': float(s.total)
        } for s in salidas])
    except SQLAlchemyError as e:
        logger.error(f"Error al listar salidas: {str(e)}")
        return jsonify({'error': str(e)}), 400

@app.route('/salidas', methods=['POST'])
def crear_salida():
    try:
        data = request.json
        nueva_salida = Salidas1(
            Numero=data['numero'],
            Mes=data['mes'],
            IdBodega=data['id_bodega'],
            FechaCreacion=data['fecha_creacion'],
            IdUsuario=data['id_usuario'],
            CuentaDebito=data.get('cuenta_debito'),
            CuentaCredito=data.get('cuenta_credito'),
            Observaciones=data.get('observaciones'),
            Recibe=data.get('recibe'),
            idproyecto=data.get('id_proyecto'),
            op=data.get('op'),
            fecha=data.get('fecha'),
            numtraslado=data.get('num_traslado')
        )
        db.session.add(nueva_salida)
        db.session.commit()
        return jsonify({'mensaje': 'Salida creada con éxito', 'numero': nueva_salida.Numero}), 201
    except SQLAlchemyError as e:
        db.session.rollback()
        logger.error(f"Error al crear salida: {str(e)}")
        return jsonify({'error': str(e)}), 400

#-----------------------------------------------------------------------------------------#

# Rutas HTML
# Ruta para la página de crear orden de compra

scheduler = BackgroundScheduler()
scheduler.start()

# Ruta para obtener todas las órdenes de compra
@app.route('/api/ordenes-compra/<numero>', methods=['GET'])
def obtener_orden(numero):
    try:
        orden = OrdenesCompra1.query.options(joinedload(OrdenesCompra1.cliente), joinedload(OrdenesCompra1.detalles)).get(numero)
        if not orden:
            return jsonify({'error': 'Orden no encontrada'}), 404
        
        return jsonify({
            'Numero': orden.Numero,
            'FechaCreacion': orden.FechaCreacion.isoformat(),
            'Estado': orden.Estado,
            'Observaciones': orden.Observaciones,
            'TiempoPreparacion': orden.TiempoPreparacion,
            'HoraEntrega': orden.HoraEntrega.isoformat() if orden.HoraEntrega else None,
            'HoraRecogida': orden.HoraRecogida.isoformat() if orden.HoraRecogida else None,
            'Direccion': orden.Direccion,
            'Referencias': [{
                'IdReferencia': ref.IdReferencia,
                'Descripcion': ref.Descripcion,
                'CantidadPedida': float(ref.CantidadPedida),
                'ObservacionesRef': ref.ObservacionesRef
            } for ref in orden.detalles],
            'Cliente': {
                'IdCliente': orden.cliente.IdCliente,
                'Nombre': orden.cliente.Nombre,
                'TipoIdentificacion': orden.cliente.TipoIdentificacion
            } if orden.cliente else None
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/ordenes-compra', methods=['GET'])
def obtener_ordenes_compra():
    try:
        ordenes = OrdenesCompra1.query.options(joinedload(OrdenesCompra1.cliente)).all()
        return jsonify([{
            'Numero': o.Numero,
            'FechaCreacion': o.FechaCreacion.isoformat(),
            'Estado': o.Estado,
            'Observaciones': o.Observaciones,
            'Cliente': {
                'IdCliente': o.cliente.IdCliente if o.cliente else None,
                'Nombre': o.cliente.Nombre if o.cliente else None,
                'TipoIdentificacion': o.cliente.TipoIdentificacion if o.cliente else None
            },
            'Referencias': [{
                'IdReferencia': ref.IdReferencia,
                'Descripcion': ref.Descripcion,
                'CantidadPedida': float(ref.CantidadPedida),
                'ObservacionesRef': ref.ObservacionesRef
            } for ref in o.detalles]
        } for o in ordenes])
    except Exception as e:
        return jsonify({'error': str(e)}), 500

# Ruta para confirmar la entrega de una orden
@app.route('/api/ordenes-compra/<numero>/entregar', methods=['POST'])
def entregar_orden(numero):
    try:
        data = request.json
        orden = OrdenesCompra1.query.get(numero)
        if not orden:
            return jsonify({'success': False, 'error': 'Orden no encontrada'}), 404
        
        orden.Estado = 'Entregada'
        orden.HoraEntrega = datetime.fromisoformat(data['horaEntrega'])
        orden.Direccion = data['direccion']
        
        if data['horaRecogida']:
            orden.HoraRecogida = datetime.fromisoformat(data['horaRecogida'])
            hora_reaparicion = orden.HoraRecogida - timedelta(hours=1)
            scheduler.add_job(
                func=reaparecer_orden, 
                trigger='date', 
                run_date=hora_reaparicion, 
                args=[numero]
            )
        
        db.session.commit()
        return jsonify({'success': True, 'message': 'Entrega confirmada'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

def reaparecer_orden(numero):
    with app.app_context():
        orden = OrdenesCompra1.query.get(numero)
        if orden:
            orden.Estado = 'Pendiente de Recoger'
            db.session.commit()

# Ruta para crear una nueva orden de compra
@app.route('/api/ordenes-compra', methods=['POST'])
def crear_orden_compra():
    try:
        data = request.json
        
        # Buscar el cliente o crear uno nuevo si no existe
        cliente = Clientes.query.get(data['id_cliente'])
        if not cliente:
            cliente = Clientes(
                IdCliente=data['id_cliente'],
                TipoIdentificacion=data['tipo_identificacion'],
                Nombre=data['nombre_cliente']
            )
            db.session.add(cliente)
        else:
            # Actualizar el nombre del cliente si es necesario
            if cliente.Nombre != data['nombre_cliente']:
                cliente.Nombre = data['nombre_cliente']

        nueva_orden = OrdenesCompra1(
            Numero=data['numero'],
            FechaCreacion=datetime.fromisoformat(data['fecha_creacion']),
            IdUsuario=data['id_usuario'],
            Observaciones=data['observaciones'],
            Estado='Creada',
            IdCliente=cliente.IdCliente
        )
        db.session.add(nueva_orden)
        
        for ref in data['referencias']:
            nueva_ref = OrdenesCompra2(
                Numero=data['numero'],
                IdReferencia=ref['id_referencia'],
                Descripcion=ref['descripcion'],
                CantidadPedida=ref['cantidad'],
                ObservacionesRef=ref['observaciones']
            )
            db.session.add(nueva_ref)
        
        db.session.commit()
        return jsonify({'success': True, 'message': 'Orden de compra creada exitosamente'}), 201
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

# Ruta para iniciar la preparación de una orden
@app.route('/api/ordenes-compra/<numero>/preparar', methods=['POST'])
def preparar_orden(numero):
    try:
        orden = OrdenesCompra1.query.get(numero)
        if not orden:
            return jsonify({'success': False, 'error': 'Orden no encontrada'}), 404
        orden.Estado = 'En preparación'
        db.session.commit()
        return jsonify({'success': True, 'message': 'Preparación iniciada'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

# Ruta para finalizar la preparación de una orden
@app.route('/api/ordenes-compra/<numero>/finalizar', methods=['POST'])
def finalizar_orden(numero):
    try:
        orden = OrdenesCompra1.query.get(numero)
        if not orden:
            return jsonify({'success': False, 'error': 'Orden no encontrada'}), 404
        orden.Estado = 'Lista'
        db.session.commit()
        return jsonify({'success': True, 'message': 'Preparación finalizada'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

# Ruta para cancelar una orden
@app.route('/api/ordenes-compra/<numero>/cancelar', methods=['POST'])
def cancelar_orden(numero):
    try:
        orden = OrdenesCompra1.query.get(numero)
        if not orden:
            return jsonify({'success': False, 'error': 'Orden no encontrada'}), 404
        orden.Estado = 'Cancelada'
        db.session.commit()
        return jsonify({'success': True, 'message': 'Orden cancelada'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

#-----------------------------------------------------------------------------------------#

# Ruta para obtener notificaciones
@app.route('/api/notificaciones', methods=['GET'])
def obtener_notificaciones():
    notificaciones = [
        {'mensaje': 'Nueva orden de compra creada'},
        {'mensaje': 'Orden lista para entrega'}
    ]
    return jsonify(notificaciones)

@app.route('/entradas.html')
@login_required
def entradas():
    return render_template('entradas.html')

@app.route('/salidas.html')
@login_required
def salidas():
    return render_template('salidas.html')

#-----------------------------------------------------------------------------------------#

# Ruta referencias
@app.route('/referencias.html')
@login_required
def referencias():
    return render_template('referencias.html')

# Rutas y funciones para Referencias
@app.route('/api/referencias', methods=['GET'])
def listar_referencias():
    try:
        referencias = Referencias.query.all()
        return jsonify([{
            'IdReferencia': r.IdReferencia,
            'Referencia': r.Referencia,
            'IdGrupo': r.IdGrupo,
            'IdUnidad': r.IdUnidad,
            'FechaCreacion': r.FechaCreacion.isoformat() if r.FechaCreacion else None,
            'Ubicacion': r.Ubicacion,
            'Estado': r.Estado,
            'Tipo': r.Tipo,
            'Costo': float(r.Costo) if r.Costo is not None else None,
            'PrecioVenta1': float(r.PrecioVenta1) if r.PrecioVenta1 is not None else None,
            'Observaciones': r.Observaciones
        } for r in referencias])
    except SQLAlchemyError as e:
        logging.error(f"Error al listar referencias: {str(e)}")
        return jsonify({'error': 'Error al obtener las referencias'}), 500

@app.route('/api/referencias', methods=['POST'])
def crear_referencia():
    try:
        data = request.json
        logging.info(f"Datos recibidos: {data}")
        
        # Asignar la fecha de creación actual
        fecha_creacion = datetime.now()
        
        nueva_referencia = Referencias(
            IdReferencia=data['IdReferencia'],
            Referencia=data['Referencia'],
            IdGrupo=data['IdGrupo'],
            IdUnidad=data['IdUnidad'],
            Ubicacion=data.get('Ubicacion', ''),
            Estado=data.get('Estado', True),  # Valor por defecto True si no se proporciona
            Tipo=data.get('Tipo', False),  # Valor por defecto False si no se proporciona
            Costo=data.get('Costo', 0),
            PrecioVenta1=data.get('PrecioVenta1', 0),
            Observaciones=data.get('Observaciones', ''),
            FechaCreacion=fecha_creacion  # Asignar la fecha de creación
        )
        db.session.add(nueva_referencia)
        
        # Actualizar el último código del grupo
        grupo = Grupo.query.get(data['IdGrupo'])
        if grupo:
            grupo.ultimoCodigo = data['ultimoCodigo']
        
        db.session.commit()
        logging.info("Referencia creada exitosamente")
        return jsonify({'success': True, 'message': 'Referencia creada exitosamente'}), 201
    except SQLAlchemyError as e:
        db.session.rollback()
        logging.error(f"Error de SQLAlchemy: {str(e)}")
        return jsonify({'success': False, 'error': str(e)}), 500
    except KeyError as e:
        logging.error(f"Error de KeyError: {str(e)}")
        return jsonify({'success': False, 'error': f'Falta el campo {str(e)}'}), 400
    except Exception as e:
        db.session.rollback()
        logging.error(f"Error inesperado: {str(e)}")
        return jsonify({'success': False, 'error': 'Error inesperado al crear la referencia'}), 500

@app.route('/api/referencias/<id>', methods=['GET'])
def obtener_referencia(id):
    try:
        referencia = Referencias.query.get(id)
        if referencia:
            return jsonify({
                'IdReferencia': referencia.IdReferencia,
                'Referencia': referencia.Referencia,
                'IdGrupo': referencia.IdGrupo,
                'IdUnidad': referencia.IdUnidad,
                'FechaCreacion': referencia.FechaCreacion.isoformat() if referencia.FechaCreacion else None,
                'Ubicacion': referencia.Ubicacion,
                'Estado': referencia.Estado,
                'Tipo': referencia.Tipo,
                'Costo': float(referencia.Costo) if referencia.Costo is not None else None,
                'PrecioVenta1': float(referencia.PrecioVenta1) if referencia.PrecioVenta1 is not None else None,
                'Observaciones': referencia.Observaciones
            })
        return jsonify({'error': 'Referencia no encontrada'}), 404
    except SQLAlchemyError as e:
        logging.error(f"Error de SQLAlchemy al obtener referencia: {str(e)}")
        return jsonify({'error': 'Error al obtener la referencia'}), 500
    except Exception as e:
        logging.error(f"Error inesperado al obtener referencia: {str(e)}")
        return jsonify({'error': 'Error inesperado al obtener la referencia'}), 500

@app.route('/api/referencias/<id>', methods=['DELETE'])
def eliminar_referencia(id):
    try:
        referencia = Referencias.query.get(id)
        if referencia:
            db.session.delete(referencia)
            db.session.commit()
            return jsonify({'success': True, 'message': 'Referencia eliminada exitosamente'}), 200
        else:
            return jsonify({'success': False, 'error': 'Referencia no encontrada'}), 404
    except SQLAlchemyError as e:
        db.session.rollback()
        logging.error(f"Error de SQLAlchemy al eliminar referencia: {str(e)}")
        return jsonify({'success': False, 'error': 'Error al eliminar la referencia'}), 500
    except Exception as e:
        db.session.rollback()
        logging.error(f"Error inesperado al eliminar referencia: {str(e)}")
        return jsonify({'success': False, 'error': 'Error inesperado al eliminar la referencia'}), 500

@app.route('/api/grupos-unidades', methods=['GET'])
def obtener_grupos_unidades():
    try:
        grupos = Grupo.query.all()
        unidades = Unidades.query.all()
        return jsonify({
            'grupos': [{'IdGrupo': g.IdGrupo, 'Grupo': g.Grupo, 'ultimoCodigo': g.ultimoCodigo} for g in grupos],
            'unidades': [{'IdUnidad': u.IdUnidad, 'Unidad': u.Unidad} for u in unidades]
        })
    except SQLAlchemyError as e:
        logger.error(f"Error al obtener grupos y unidades: {str(e)}")
        return jsonify({'error': 'Error al obtener grupos y unidades'}), 500

#-----------------------------------------------------------------------------------------#

# Ruta Grupos
@app.route('/grupos.html')
@login_required
def grupos():
    grupos = Grupo.query.all()
    return render_template('grupos.html', grupos=grupos)

@app.route('/nuevo_grupo.html')
@login_required
def nuevo_grupo():
    return render_template('nuevo_grupo.html')

@app.route('/editar_grupo.html/<int:id>')
@login_required
def editar_grupo(id):
    return render_template('editar_grupo.html', id=id)

# Crear un nuevo grupo
@app.route('/api/grupos', methods=['POST'])
def crear_grupo():
    try:
        data = request.json
        nuevo_grupo = Grupo(
            IdGrupo=data['IdGrupo'],
            Grupo=data['Grupo'],
            Estado=data['Estado'],
            ultimoCodigo=data['UltimoCodigo']
        )
        db.session.add(nuevo_grupo)
        db.session.commit()
        return jsonify({'success': True, 'message': 'Grupo creado exitosamente', 'id': nuevo_grupo.IdGrupo}), 201
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

# Obtener todos los grupos
@app.route('/api/grupos', methods=['GET'])
def obtener_grupos():
    try:
        grupos = Grupo.query.all()
        return jsonify([{
            'IdGrupo': g.IdGrupo,
            'Grupo': g.Grupo,
            'Estado': g.Estado,
            'ultimoCodigo': g.ultimoCodigo
        } for g in grupos])
    except Exception as e:
        return jsonify({'error': str(e)}), 400

# Obtener un grupo específico
@app.route('/api/grupos/<id>', methods=['GET'])
def obtener_grupo(id):
    try:
        grupo = Grupo.query.get(id)
        if grupo:
            return jsonify({
                'IdGrupo': grupo.IdGrupo,
                'Grupo': grupo.Grupo,
                'Estado': grupo.Estado,
                'ultimoCodigo': grupo.ultimoCodigo
            })
        return jsonify({'error': 'Grupo no encontrado'}), 404
    except Exception as e:
        return jsonify({'error': str(e)}), 400

# Actualizar un grupo
@app.route('/api/grupos/<id>', methods=['PUT'])
def actualizar_grupo(id):
    try:
        grupo = Grupo.query.get(id)
        if not grupo:
            return jsonify({'error': 'Grupo no encontrado'}), 404
        
        data = request.json
        grupo.Grupo = data.get('Grupo', grupo.Grupo)
        grupo.Estado = data.get('Estado', grupo.Estado)
        grupo.ultimoCodigo = data.get('ultimoCodigo', grupo.ultimoCodigo)
        
        db.session.commit()
        return jsonify({'success': True, 'message': 'Grupo actualizado exitosamente'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

# Eliminar un grupo
@app.route('/api/grupos/<id>', methods=['DELETE'])
def eliminar_grupo(id):
    try:
        grupo = Grupo.query.get(id)
        if not grupo:
            return jsonify({'error': 'Grupo no encontrado'}), 404
        
        db.session.delete(grupo)
        db.session.commit()
        return jsonify({'success': True, 'message': 'Grupo eliminado exitosamente'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

#-----------------------------------------------------------------------------------------#

# Nuevas rutas para Unidades
@app.route('/unidades.html')
@login_required
def unidades():
    unidades = Unidades.query.all()
    return render_template('unidades.html', unidades=unidades)

@app.route('/nueva_unidad.html')
@login_required
def nueva_unidad():
    return render_template('nueva_unidad.html')

# Crear una nueva unidad
@app.route('/api/unidades', methods=['POST'])
def crear_unidad():
    try:
        data = request.json
        nueva_unidad = Unidades(
            IdUnidad=data['IdUnidad'],
            Unidad=data['Unidad'],
            Estado=data['Estado']
        )
        db.session.add(nueva_unidad)
        db.session.commit()
        return jsonify({'success': True, 'message': 'Unidad creada exitosamente', 'id': nueva_unidad.IdUnidad}), 201
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

# Obtener todas las unidades
@app.route('/api/unidades', methods=['GET'])
def obtener_unidades():
    try:
        unidades = Unidades.query.all()
        return jsonify([{
            'IdUnidad': u.IdUnidad,
            'Unidad': u.Unidad,
            'Estado': u.Estado
        } for u in unidades])
    except Exception as e:
        return jsonify({'error': str(e)}), 400

# Obtener una unidad específica
@app.route('/api/unidades/<id>', methods=['GET'])
def obtener_unidad(id):
    try:
        unidad = Unidades.query.get(id)
        if unidad:
            return jsonify({
                'IdUnidad': unidad.IdUnidad,
                'Unidad': unidad.Unidad,
                'Estado': unidad.Estado
            })
        return jsonify({'error': 'Unidad no encontrada'}), 404
    except Exception as e:
        return jsonify({'error': str(e)}), 400

# Actualizar una unidad
@app.route('/api/unidades/<id>', methods=['PUT'])
def actualizar_unidad(id):
    try:
        unidad = Unidades.query.get(id)
        if not unidad:
            return jsonify({'error': 'Unidad no encontrada'}), 404
        
        data = request.json
        unidad.Unidad = data.get('Unidad', unidad.Unidad)
        unidad.Estado = data.get('Estado', unidad.Estado)
        
        db.session.commit()
        return jsonify({'success': True, 'message': 'Unidad actualizada exitosamente'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

# Eliminar una unidad
@app.route('/api/unidades/<id>', methods=['DELETE'])
def eliminar_unidad(id):
    try:
        unidad = Unidades.query.get(id)
        if not unidad:
            return jsonify({'error': 'Unidad no encontrada'}), 404
        
        db.session.delete(unidad)
        db.session.commit()
        return jsonify({'success': True, 'message': 'Unidad eliminada exitosamente'})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 400

#-----------------------------------------------------------------------------------------#

# Nuevas rutas para Configuración
@app.route('/configuracion', methods=['GET', 'POST'])
@login_required
def configuracion():
    if request.method == 'POST':
        password = request.form.get('password')
        if password == 'smig123*/':
            return redirect(url_for('configuracion_consecutivos'))
        else:
            flash('Contraseña incorrecta', 'error')
    return render_template('configuracion.html')

@app.route('/configuracion_consecutivos')
@login_required
def configuracion_consecutivos():
    try:
        consecutivos = Consecutivos.query.all()
        if not consecutivos:
            flash('No se encontraron consecutivos en la base de datos.', 'info')
        return render_template('configuracion_consecutivos.html', consecutivos=consecutivos)
    except SQLAlchemyError as e:
        error = str(e.__dict__['orig'])
        app.logger.error(f"Error de base de datos: {error}")
        flash(f'Error al acceder a la base de datos: {error}', 'error')
    except Exception as e:
        app.logger.error(f"Error inesperado: {str(e)}")
        app.logger.error(traceback.format_exc())
        flash(f'Ocurrió un error inesperado: {str(e)}', 'error')
    return redirect(url_for('configuracion'))

# Guardar Consecutivos
@app.route('/api/guardar_consecutivo', methods=['POST'])
def api_guardar_consecutivo():
    try:
        data = request.json
        nuevo_consecutivo = Consecutivos(
            IdConsecutivo=data['IdConsecutivo'],
            Consecutivo=data['Consecutivo'],
            Formulario=data['Formulario'],
            Prefijo=data['Prefijo'],
            Desde=data['Desde'],
            Hasta=data['Hasta'],
            Actual=data['Actual'],
            Resolucion=data.get('Resolucion'),
            Estado=data['Estado']
        )
        db.session.add(nuevo_consecutivo)
        db.session.commit()
        return jsonify({"success": True, "message": "Consecutivo guardado exitosamente"})
    except Exception as e:
        db.session.rollback()
        return jsonify({"success": False, "error": str(e)}), 400

# Editar Consecutivos
@app.route('/editar_consecutivo/<int:id>', methods=['GET', 'POST'])
@login_required
def editar_consecutivo(id):
    consecutivo = Consecutivos.query.get_or_404(id)
    if request.method == 'POST':
        consecutivo.Consecutivo = request.form['Consecutivo']
        consecutivo.Formulario = request.form['Formulario']
        consecutivo.Prefijo = request.form['Prefijo']
        consecutivo.Desde = request.form['Desde']
        consecutivo.Hasta = request.form['Hasta']
        consecutivo.Actual = request.form['Actual']
        consecutivo.Resolucion = request.form['Resolucion']
        consecutivo.FechaResolucion = request.form['FechaResolucion']
        consecutivo.ObservacionesResolucion = request.form['ObservacionesResolucion']
        consecutivo.Estado = 'Estado' in request.form
        consecutivo.Comprobante = request.form['Comprobante']
        consecutivo.Predeterminado = request.form['Predeterminado']
        consecutivo.fechafinresolucion = request.form['fechafinresolucion']
        consecutivo.tiporesolucion = request.form['tiporesolucion']

        try:
            db.session.commit()
            flash('Consecutivo actualizado exitosamente', 'success')
            return redirect(url_for('configuracion_consecutivos'))
        except Exception as e:
            db.session.rollback()
            flash(f'Error al actualizar el consecutivo: {str(e)}', 'error')

    return render_template('editar_consecutivo.html', consecutivo=consecutivo)

# Eliminar Consecutivos
@app.route('/eliminar_consecutivo/<int:id>')
def eliminar_consecutivo(id):
    consecutivo = Consecutivos.query.get_or_404(id)
    try:
        db.session.delete(consecutivo)
        db.session.commit()
        flash('Consecutivo eliminado exitosamente', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'Error al eliminar el consecutivo: {str(e)}', 'error')
    return redirect(url_for('configuracion_consecutivos'))

#-----------------------------------------------------------------------------------------#

# Manejador de errores
@app.errorhandler(500)
def internal_error(error):
    logger.error('Server Error: %s', (error))
    return "Error interno del servidor", 500

@app.errorhandler(Exception)
def unhandled_exception(e):
    logger.error('Unhandled Exception: %s', (e), exc_info=True)
    return "Error inesperado: {}".format(str(e)), 500

#-----------------------------------------------------------------------------------------#

if __name__ == '__main__':
    socketio.run(app, debug=True)