import mysql.connector
from datetime import datetime

class Database:
    def __init__(self):
        self.connection = mysql.connector.connect(
            host="192.168.1.2",
            user="root",
            password="Perla17*+",
            database="kardex"
        )
        self.cursor = self.connection.cursor(dictionary=True)
    
    def execute_query(self, query, params=None):
        self.cursor.execute(query, params or ())
        return self.cursor.fetchall()
    
    def execute_insert(self, query, params=None):
        self.cursor.execute(query, params or ())
        self.connection.commit()
        return self.cursor.lastrowid
    
    def get_next_auto_increment(self, table_name):
        self.cursor.execute(f"SHOW TABLE STATUS LIKE '{table_name}'")
        result = self.cursor.fetchone()
        return result['Auto_increment']

class UnidadProducto:
    def __init__(self, db):
        self.db = db
    
    def listar(self):
        return self.db.execute_query("SELECT * FROM unidadesproducto")
    
    def obtener_por_id(self, id_unidad):
        result = self.db.execute_query(
            "SELECT * FROM unidadesproducto WHERE idUnidad = %s",
            (id_unidad,)
        )
        return result[0] if result else None

class Producto:
    def __init__(self, db):
        self.db = db
    
    def listar(self):
        return self.db.execute_query("""
            SELECT p.*, u.nombreUnidad 
            FROM productos p
            JOIN unidadesproducto u ON p.idUnidadBase = u.idUnidad
        """)
    
    def obtener(self, id_producto):
        result = self.db.execute_query(
            "SELECT * FROM productos WHERE idProducto = %s",
            (id_producto,)
        )
        return result[0] if result else None
    
    def obtener_equivalencias(self, id_producto):
        return self.db.execute_query(
            "SELECT * FROM equivalencias WHERE idProducto = %s",
            (id_producto,)
        )
    
    def existe_nombre(self, nombre):
        result = self.db.execute_query(
            "SELECT idProducto FROM productos WHERE nombreProducto = %s",
            (nombre,)
        )
        return bool(result)
    
    def insertar(self, codigo, nombre, id_unidad_base, stock):
        return self.db.execute_insert(
            """INSERT INTO productos 
            (codigoProducto, nombreProducto, idUnidadBase, stockInicial) 
            VALUES (%s, %s, %s, %s)""",
            (codigo, nombre, id_unidad_base, stock)
        )
    
    def obtener_movimientos(self):
        return self.db.execute_query("""
            SELECT p.idProducto, 
                   SUM(CASE WHEN m.tipoMovimiento = 'ENTRADA' THEN m.cantidad ELSE 0 END) as entradas,
                   SUM(CASE WHEN m.tipoMovimiento = 'SALIDA' THEN m.cantidad ELSE 0 END) as salidas
            FROM productos p
            LEFT JOIN movimientos m ON p.idProducto = m.idProducto
            GROUP BY p.idProducto
        """)

def print_tabla(header, rows):
    if not rows:
        print("No hay datos para mostrar")
        return
    
    column_widths = [len(str(campo)) for campo in header]
    for row in rows:
        for i, campo in enumerate(row):
            column_widths[i] = max(column_widths[i], len(str(campo)))
    
    border = "+" + "+".join(["-" * (w + 2) for w in column_widths]) + "+"
    row_format = "| " + " | ".join(["{:<" + str(w) + "}" for w in column_widths]) + " |"
    
    print(border)
    print(row_format.format(*header))
    print(border)
    for row in rows:
        print(row_format.format(*row))
    print(border)

def registrar_unidades(db):
    print("\nREGISTRO DE UNIDADES (0 para volver)")
    nombre = input("Nombre de la unidad: ").strip()
    if nombre == "0":
        return
    if not nombre:
        print("El nombre no puede estar vacío")
        return
    
    try:
        db.execute_insert(
            "INSERT INTO unidadesproducto (nombreUnidad) VALUES (%s)",
            (nombre,)
        )
        print("\n¡Unidad registrada exitosamente!")
    except Exception as e:
        print(f"\nError al registrar unidad: {str(e)}")

def listar_unidades(db):
    unidades = UnidadProducto(db).listar()
    if not unidades:
        print("\nNo hay unidades registradas")
        return
    
    header = ["N°", "NOMBRE DE UNIDAD"]
    rows = [(str(i+1), u['nombreUnidad']) for i, u in enumerate(unidades)]
    
    print("\nLISTADO DE UNIDADES REGISTRADAS")
    print_tabla(header, rows)

def listar_productos(db):
    productos = Producto(db).listar()
    if not productos:
        print("\nNo hay productos registrados")
        return
    
    header = ["N°", "CÓDIGO", "NOMBRE DEL PRODUCTOS", "UNIDAD BASE"]
    rows = []
    for i, p in enumerate(productos, 1):
        rows.append((
            f"{i:02d}",
            p['codigoProducto'],
            p['nombreProducto'],
            p['nombreUnidad']
        ))
    
    print("\nLISTADO DE PRODUCTOS REGISTRADOS")
    print_tabla(header, rows)

def registrar_producto(db):
    print("\nREGISTRO DE PRODUCTO (0 para volver)")
    producto = Producto(db)
    unidades = UnidadProducto(db)
    
    while True:
        nombre = input("Nombre del producto: ").strip()
        if nombre == "0":
            return
        if not nombre:
            print("El nombre no puede estar vacío")
            continue
        if producto.existe_nombre(nombre):
            print("¡El producto ya existe!")
        else:
            break

    # Continúa en la siguiente parte...


    while True:
        try:
            num_medidas = int(input("\n¿Cuántas medidas para cuantificar el producto? (1-4): "))
            if 1 <= num_medidas <= 4: 
                break
            print("Debe ser un número entre 1 y 4")
        except ValueError:
            print("Entrada inválida, debe ser un número")
    
    unidades_seleccionadas = []
    for i in range(num_medidas):
        while True:
            unidades_disponibles = unidades.listar()
            print(f"\nSelección de medida {i+1}:")
            print("Unidades disponibles:")
            
            for idx, u in enumerate(unidades_disponibles, 1):
                print(f"{idx}. {u['nombreUnidad']}")
            print(f"{len(unidades_disponibles)+1}. Agregar nueva unidad")
            
            try:
                opcion = int(input("Seleccione una opción: "))
                if 1 <= opcion <= len(unidades_disponibles):
                    unidades_seleccionadas.append(unidades_disponibles[opcion-1])
                    break
                elif opcion == len(unidades_disponibles)+1:
                    nombre_unidad = input("Nombre de la nueva unidad: ").strip()
                    if not nombre_unidad:
                        print("El nombre no puede estar vacío")
                        continue
                    db.execute_insert(
                        "INSERT INTO unidadesproducto (nombreUnidad) VALUES (%s)",
                        (nombre_unidad,)
                    )
                    print("¡Nueva unidad registrada!")
                else:
                    print("Opción inválida")
            except ValueError:
                print("Entrada inválida, debe ser un número")
    
    equivalencias = []
    for i in range(num_medidas-1):
        while True:
            try:
                cantidad = int(input(
                    f"\n1 {unidades_seleccionadas[i+1]['nombreUnidad']} = ¿Cuántas {unidades_seleccionadas[i]['nombreUnidad']}? "
                ))
                if cantidad > 0:
                    equivalencias.append((
                        unidades_seleccionadas[i+1]['idUnidad'],
                        unidades_seleccionadas[i]['idUnidad'],
                        cantidad
                    ))
                    break
                print("La cantidad debe ser mayor a 0")
            except ValueError:
                print("Entrada inválida, debe ser un número")
    
    while True:
        print("\nSelección de unidad para stock inicial:")
        for idx, u in enumerate(unidades_seleccionadas, 1):
            print(f"{idx}. {u['nombreUnidad']}")
        try:
            opcion = int(input("Seleccione una opción: ")) - 1
            if 0 <= opcion < len(unidades_seleccionadas):
                unidad_stock = unidades_seleccionadas[opcion]
                stock = int(input("Stock inicial: "))
                if stock < 0:
                    print("El stock no puede ser negativo")
                    continue
                break
        except ValueError:
            print("Entrada inválida")
    
    factor = 1
    for i in range(opcion):
        factor *= equivalencias[i][2]
    stock_base = stock * factor
    
    next_id = db.get_next_auto_increment('productos')
    codigo = f"ACU-{next_id:02d}"
    
    id_producto = producto.insertar(
        codigo,
        nombre,
        unidades_seleccionadas[0]['idUnidad'],
        stock_base
    )
    
    for sup, inf, cant in equivalencias:
        db.execute_insert(
            """INSERT INTO equivalencias 
            (idProducto, idUnidadSuperior, idUnidadInferior, cantidadEquivalente) 
            VALUES (%s, %s, %s, %s)""",
            (id_producto, sup, inf, cant)
        )
    
    fecha_registro = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    header = ["CÓDIGO", "NOMBRE DEL PRODUCTO", "STOCK", "UNIDAD", "F. REGISTRO"]
    row = [codigo, nombre, str(stock), unidad_stock['nombreUnidad'], fecha_registro]
    
    print("\n¡PRODUCTO REGISTRADO EXITOSAMENTE!")
    print_tabla(header, [row])

def registrar_entrada(db):
    print("\nREGISTRO DE ENTRADA DE PRODUCTOS (0 para volver)")
    
    productos = Producto(db).listar()
    if not productos:
        print("No hay productos registrados")
        return
    
    print("\nSeleccione un producto:")
    header = ["N°", "CÓDIGO", "NOMBRE DEL PRODUCTO", "STOCK", "UNIDAD"]
    rows = [(str(i+1), p['codigoProducto'], p['nombreProducto'], str(p['stockInicial']), p['nombreUnidad']) 
            for i, p in enumerate(productos)]
    print_tabla(header, rows)
    
    try:
        opcion = input("\nIngrese el número de producto (0 para volver): ").strip()
        if opcion == "0":
            return
        opcion = int(opcion) - 1
        producto = productos[opcion]
    except (ValueError, IndexError):
        print("Selección inválida")
        return
    
    unidades_ids = {producto['idUnidadBase']}
    equivalencias = db.execute_query(
        "SELECT idUnidadSuperior, idUnidadInferior FROM equivalencias WHERE idProducto = %s",
        (producto['idProducto'],)
    )
    for eq in equivalencias:
        unidades_ids.add(eq['idUnidadSuperior'])
        unidades_ids.add(eq['idUnidadInferior'])
    
    unidades = []
    for uid in unidades_ids:
        unidad = db.execute_query(
            "SELECT * FROM unidadesproducto WHERE idUnidad = %s",
            (uid,)
        )
        if unidad:
            unidades.append(unidad[0])
    
    if not unidades:
        print("No hay unidades asociadas al producto")
        return
    
    print("\nUnidades disponibles para este producto:")
    header = ["N°", "NOMBRE DE UNIDAD"]
    rows = [(str(i+1), u['nombreUnidad']) for i, u in enumerate(unidades)]
    print_tabla(header, rows)
    
    try:
        opcion_unidad = int(input("\nIngrese el número de unidad: ")) - 1
        unidad = unidades[opcion_unidad]
    except (ValueError, IndexError):
        print("Selección inválida")
        return
    
    factor = 1
    current_unit = unidad['idUnidad']
    base_unit = producto['idUnidadBase']
    equivalencias = db.execute_query(
        "SELECT * FROM equivalencias WHERE idProducto = %s",
        (producto['idProducto'],)
    )
    
    while current_unit != base_unit:
        encontrado = False
        for eq in equivalencias:
            if eq['idUnidadSuperior'] == current_unit:
                factor *= eq['cantidadEquivalente']
                current_unit = eq['idUnidadInferior']
                encontrado = True
                break
        if not encontrado:
            print("No se puede convertir a la unidad base")
            return
    
    try:
        stock = int(input("\nStock de ingreso: "))
        if stock <= 0:
            print("El stock debe ser mayor a 0")
            return
    except ValueError:
        print("Valor inválido")
        return
    
    documentos = db.execute_query("SELECT * FROM documentos")
    print("\nSeleccione tipo de documento:")
    for i, doc in enumerate(documentos, 1):
        print(f"{i}. {doc['nombreDocumento']}")
    
    try:
        doc_opcion = int(input("Opción: ")) - 1
        documento = documentos[doc_opcion]
    except (ValueError, IndexError):
        print("Selección inválida")
        return
    
    numero_doc = input("Número de documento: ").strip()
    
    # Seleccionar proveedor
    proveedores = db.execute_query(
        "SELECT * FROM entidades WHERE tipoEntidad = 'EMPRESA'"
    )
    if not proveedores:
        print("\nNo hay proveedores registrados. Registre uno primero.")
        return
    
    print("\nLISTADO DE PROVEEDORES REGISTRADOS")
    header = ["N°", "TIPO", "DOCUMENTO", "NOMBRE"]
    rows = [(str(i+1), p['tipoDocumento'], p['numeroDocumento'], p['nomEntidad']) 
            for i, p in enumerate(proveedores)]
    print_tabla(header, rows)
    
    try:
        opcion = int(input("\nSeleccione proveedor (0 para cancelar): "))
        if opcion == 0:
            return
        proveedor = proveedores[opcion-1]['numeroDocumento']
    except:
        print("Selección inválida")
        return

    fecha = input("Ingrese fecha (DD-MM-AA) o 0 para fecha actual: ").strip()
    if fecha == '0':
        fecha_movimiento = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    else:
        try:
            fecha_dt = datetime.strptime(fecha, '%d-%m-%y')
            fecha_movimiento = fecha_dt.strftime('%Y-%m-%d %H:%M:%S')
        except ValueError:
            print("Formato de fecha inválido, usando fecha actual")
            fecha_movimiento = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    
    stock_base = stock * factor
    
    try:
        db.execute_insert(
            """INSERT INTO movimientos 
            (tipoMovimiento, idProducto, cantidad, idDocumento, 
             numeroDocumento, proveedor, fechaMovimiento)
            VALUES (%s, %s, %s, %s, %s, %s, %s)""",
            ('ENTRADA', producto['idProducto'], stock_base, 
             documento['idDocumento'], numero_doc, proveedor, fecha_movimiento)
        )
        print("\n¡Entrada registrada exitosamente!")
    except Exception as e:
        print(f"Error al registrar entrada: {str(e)}")

# Continúa en la siguiente parte...












def registrar_salida(db):
    print("\nREGISTRO DE SALIDA DE PRODUCTOS (0 para volver)")
    
    productos = Producto(db).listar()
    if not productos:
        print("No hay productos registrados")
        return
    
    print("\nSeleccione un producto:")
    header = ["N°", "CÓDIGO", "NOMBRE DEL PRODUCTO", "STOCK", "UNIDAD"]
    rows = [(str(i+1), p['codigoProducto'], p['nombreProducto'], str(p['stockInicial']), p['nombreUnidad']) 
            for i, p in enumerate(productos)]
    print_tabla(header, rows)
    
    try:
        opcion = input("\nIngrese el número de producto (0 para volver): ").strip()
        if opcion == "0":
            return
        opcion = int(opcion) - 1
        producto = productos[opcion]
    except (ValueError, IndexError):
        print("Selección inválida")
        return
    
    unidades_ids = {producto['idUnidadBase']}
    equivalencias = db.execute_query(
        "SELECT idUnidadSuperior, idUnidadInferior FROM equivalencias WHERE idProducto = %s",
        (producto['idProducto'],)
    )
    for eq in equivalencias:
        unidades_ids.add(eq['idUnidadSuperior'])
        unidades_ids.add(eq['idUnidadInferior'])
    
    unidades = []
    for uid in unidades_ids:
        unidad = db.execute_query(
            "SELECT * FROM unidadesproducto WHERE idUnidad = %s",
            (uid,)
        )
        if unidad:
            unidades.append(unidad[0])
    
    if not unidades:
        print("No hay unidades asociadas al producto")
        return
    
    print("\nUnidades disponibles para este producto:")
    header = ["N°", "NOMBRE DE UNIDAD"]
    rows = [(str(i+1), u['nombreUnidad']) for i, u in enumerate(unidades)]
    print_tabla(header, rows)
    
    try:
        opcion_unidad = int(input("\nIngrese el número de unidad: ")) - 1
        unidad = unidades[opcion_unidad]
    except (ValueError, IndexError):
        print("Selección inválida")
        return
    
    factor = 1
    current_unit = unidad['idUnidad']
    base_unit = producto['idUnidadBase']
    equivalencias = db.execute_query(
        "SELECT * FROM equivalencias WHERE idProducto = %s",
        (producto['idProducto'],)
    )
    
    while current_unit != base_unit:
        encontrado = False
        for eq in equivalencias:
            if eq['idUnidadSuperior'] == current_unit:
                factor *= eq['cantidadEquivalente']
                current_unit = eq['idUnidadInferior']
                encontrado = True
                break
        if not encontrado:
            print("No se puede convertir a la unidad base")
            return
    
    try:
        stock = int(input("\nStock de salida: "))
        if stock <= 0:
            print("El stock debe ser mayor a 0")
            return
        
        stock_base_total = producto['stockInicial'] + sum(
            m['cantidad'] for m in db.execute_query(
                "SELECT cantidad FROM movimientos WHERE idProducto = %s AND tipoMovimiento = 'ENTRADA'",
                (producto['idProducto'],)
            )
        ) - sum(
            m['cantidad'] for m in db.execute_query(
                "SELECT cantidad FROM movimientos WHERE idProducto = %s AND tipoMovimiento = 'SALIDA'",
                (producto['idProducto'],)
            )
        )
        
        if (stock * factor) > stock_base_total:
            print("No hay suficiente stock disponible")
            return
            
    except ValueError:
        print("Valor inválido")
        return
    
    # Seleccionar usuario
    usuarios = db.execute_query(
        "SELECT * FROM entidades WHERE tipoEntidad = 'USUARIO'"
    )
    if not usuarios:
        print("\nNo hay usuarios registrados. Registre uno primero.")
        return
    
    print("\nLISTADO DE USUARIOS REGISTRADOS")
    header = ["N°", "TIPO", "DOCUMENTO", "NOMBRE"]
    rows = [(str(i+1), u['tipoDocumento'], u['numeroDocumento'], u['nomEntidad']) 
            for i, u in enumerate(usuarios)]
    print_tabla(header, rows)
    
    try:
        opcion = int(input("\nSeleccione usuario (0 para cancelar): "))
        if opcion == 0:
            return
        usuario = usuarios[opcion-1]['numeroDocumento']
    except:
        print("Selección inválida")
        return

    fecha = input("Ingrese fecha (DD-MM-AA) o 0 para fecha actual: ").strip()
    if fecha == '0':
        fecha_movimiento = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    else:
        try:
            fecha_dt = datetime.strptime(fecha, '%d-%m-%y')
            fecha_movimiento = fecha_dt.strftime('%Y-%m-%d %H:%M:%S')
        except ValueError:
            print("Formato de fecha inválido, usando fecha actual")
            fecha_movimiento = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    
    stock_base = stock * factor
    
    try:
        db.execute_insert(
            """INSERT INTO movimientos 
            (tipoMovimiento, idProducto, cantidad, proveedor, fechaMovimiento)
            VALUES (%s, %s, %s, %s, %s)""",
            ('SALIDA', producto['idProducto'], stock_base, usuario, fecha_movimiento)
        )
        print("\n¡Salida registrada exitosamente!")
    except Exception as e:
        print(f"Error al registrar salida: {str(e)}")

def reporte_movimientos(db):
    productos = Producto(db).listar()
    movimientos = Producto(db).obtener_movimientos()
    
    if not productos:
        print("\nNo hay productos registrados")
        return
    
    header = ["N°", "CÓDIGO", "NOMBRE DEL PRODUCTO", "STOCK BASE", 
              "STOCK INGRESO", "STOCK SALIDA", "STOCK REAL", "UNIDAD"]
    
    rows = []
    for i, p in enumerate(productos, 1):
        mov = next((m for m in movimientos if m['idProducto'] == p['idProducto']), None)
        entradas = mov['entradas'] if mov else 0
        salidas = mov['salidas'] if mov else 0
        stock_real = p['stockInicial'] + entradas - salidas
        
        rows.append((
            f"{i:02d}",
            p['codigoProducto'],
            p['nombreProducto'],
            str(p['stockInicial']),
            str(entradas),
            str(salidas),
            str(stock_real),
            p['nombreUnidad']
        ))
    
    print("\nREPORTE DE MOVIMIENTOS")
    print_tabla(header, rows)

def reporte_ingresos(db, por_producto=False):
    query = """
        SELECT m.*, p.codigoProducto, p.nombreProducto, 
               u.nombreUnidad, e.nomEntidad 
        FROM movimientos m
        JOIN productos p ON m.idProducto = p.idProducto
        JOIN unidadesproducto u ON p.idUnidadBase = u.idUnidad
        LEFT JOIN entidades e ON m.proveedor = e.numeroDocumento
        WHERE m.tipoMovimiento = 'ENTRADA'
    """
    
    if por_producto:
        productos = db.execute_query("SELECT * FROM productos")
        print("\nSeleccione un producto:")
        header = ["N°", "CÓDIGO", "NOMBRE DEL PRODUCTO"]
        rows = [(str(i+1), p['codigoProducto'], p['nombreProducto']) 
                for i, p in enumerate(productos)]
        print_tabla(header, rows)
        
        try:
            opcion = int(input("\nIngrese el número de producto: ")) - 1
            id_producto = productos[opcion]['idProducto']
            query += f" AND m.idProducto = {id_producto}"
        except:
            print("Selección inválida")
            return

    movimientos = db.execute_query(query)
    
    if not movimientos:
        print("\nNo hay movimientos registrados")
        return
    
    header = ["N°", "CÓDIGO", "PRODUCTO", "CANTIDAD", "UNIDAD", "PROVEEDOR", "FECHA"]
    rows = []
    for i, m in enumerate(movimientos, 1):
        rows.append((
            f"{i:02d}",
            m['codigoProducto'],
            m['nombreProducto'],
            str(m['cantidad']),
            m['nombreUnidad'],
            m['nomEntidad'] or 'N/A',
            m['fechaMovimiento'].strftime('%d/%m/%Y')
        ))
    
    print("\nREPORTE DE INGRESOS" + (" POR PRODUCTO" if por_producto else ""))
    print_tabla(header, rows)

def reporte_salidas(db, por_producto=False):
    query = """
        SELECT m.*, p.codigoProducto, p.nombreProducto, 
               u.nombreUnidad, e.nomEntidad 
        FROM movimientos m
        JOIN productos p ON m.idProducto = p.idProducto
        JOIN unidadesproducto u ON p.idUnidadBase = u.idUnidad
        LEFT JOIN entidades e ON m.proveedor = e.numeroDocumento
        WHERE m.tipoMovimiento = 'SALIDA'
        ORDER BY m.fechaMovimiento DESC
    """
    
    if por_producto:
        productos = db.execute_query("SELECT * FROM productos")
        print("\nSeleccione un producto:")
        header = ["N°", "CÓDIGO", "NOMBRE DEL PRODUCTO"]
        rows = [(str(i+1), p['codigoProducto'], p['nombreProducto']) 
                for i, p in enumerate(productos)]
        print_tabla(header, rows)
        
        try:
            opcion = int(input("\nIngrese el número de producto: ")) - 1
            id_producto = productos[opcion]['idProducto']
            query += f" AND m.idProducto = {id_producto}"
        except:
            print("Selección inválida")
            return

    movimientos = db.execute_query(query)
    
    if not movimientos:
        print("\nNo hay movimientos registrados")
        return
    
    header = ["N°", "CÓDIGO", "PRODUCTO", "CANTIDAD", "UNIDAD", "USUARIO", "FECHA"]
    rows = []
    for i, m in enumerate(movimientos, 1):
        rows.append((
            f"{i:02d}",
            m['codigoProducto'],
            m['nombreProducto'],
            str(m['cantidad']),
            m['nombreUnidad'],
            m['nomEntidad'] or 'N/A',
            m['fechaMovimiento'].strftime('%d/%m/%Y')
        ))
    
    print("\nREPORTE DE SALIDAS" + (" POR PRODUCTO" if por_producto else ""))
    print_tabla(header, rows)

def registrar_entidad(db):
    print("\nREGISTRO DE ENTIDAD (0 para volver)")
    
    while True:
        print("Tipo de entidad:")
        print("1. USUARIO")
        print("2. EMPRESA")
        print("0. Volver")
        opcion_tipo = input("Seleccione una opción: ").strip()
        
        if opcion_tipo == "0":
            return
        if opcion_tipo == '1':
            tipo_entidad = 'USUARIO'
            tipo_doc = 'DNI'
            break
        elif opcion_tipo == '2':
            tipo_entidad = 'EMPRESA'
            tipo_doc = 'RUC'
            break
        else:
            print("Opción inválida")

    while True:
        numero_doc = input(f"Ingrese {tipo_doc} (1 para generar DNI especial): ").strip()
        
        if tipo_doc == 'DNI':
            if numero_doc == "1":
                # Se consulta el máximo DNI especial (los que comienzan con '999')
                resultado = db.execute_query(
                    "SELECT MAX(numeroDocumento) AS maxDNI FROM entidades WHERE numeroDocumento LIKE '999%'"
                )
                if resultado and resultado[0]['maxDNI'] is not None:
                    nuevo_dni = int(resultado[0]['maxDNI']) + 1
                else:
                    nuevo_dni = 99900001  # Valor inicial si no existe ningún DNI especial
                numero_doc = str(nuevo_dni)
            elif numero_doc == "0":
                return
        
        # Si el número ingresado NO es un DNI especial (no comienza con "999"),
        # se verifica que no exista ya en la base de datos.
        if not numero_doc.startswith("999"):
            existe = db.execute_query(
                "SELECT idEntidad FROM entidades WHERE numeroDocumento = %s",
                (numero_doc,)
            )
            if existe:
                print(f"¡El {tipo_doc} ya existe en el sistema!")
                continue
       
        if tipo_doc == 'DNI' and len(numero_doc) == 8 and numero_doc.isdigit():
            break
        elif tipo_doc == 'RUC' and len(numero_doc) == 11 and numero_doc.isdigit():
            break
        print(f"{tipo_doc} inválido. Debe tener {8 if tipo_doc == 'DNI' else 11} dígitos")

    nombre_entidad = input("Nombre de la entidad: ").strip()

    try:
        db.execute_insert(
            """INSERT INTO entidades 
            (tipoEntidad, tipoDocumento, numeroDocumento, nomEntidad) 
            VALUES (%s, %s, %s, %s)""",
            (tipo_entidad, tipo_doc, numero_doc, nombre_entidad)
        )
        print("\n¡Entidad registrada exitosamente!")
        
        entidad = db.execute_query(
            "SELECT * FROM entidades ORDER BY idEntidad DESC LIMIT 1"
        )[0]
        
        header = ["N°", "TIPOENTIDAD", "TIPODOCUMENTO", "NUMERODOCUMENTO", "NOMENTIDAD", "FECHAREGISTRO"]
        rows = [(
            "01",
            entidad['tipoEntidad'],
            entidad['tipoDocumento'],
            entidad['numeroDocumento'],
            entidad['nomEntidad'],
            entidad['fechaRegistro'].strftime('%d/%m/%Y')
        )]
        print_tabla(header, rows)
        
    except mysql.connector.IntegrityError:
        print("\nError: El número de documento ya existe en el sistema")
    except Exception as e:
        print(f"\nError al registrar entidad: {str(e)}")

def listar_entidades(db):
    entidades = db.execute_query("SELECT * FROM entidades")
    if not entidades:
        print("\nNo hay entidades registradas")
        return
    
    header = ["N°", "TIPOENTIDAD", "TIPODOCUMENTO", "NUMERODOCUMENTO", "NOMENTIDAD", "FECHAREGISTRO"]
    rows = []
    for i, entidad in enumerate(entidades, 1):
        rows.append((
            f"{i:02d}",
            entidad['tipoEntidad'],
            entidad['tipoDocumento'],
            entidad['numeroDocumento'],
            entidad['nomEntidad'],
            entidad['fechaRegistro'].strftime('%d/%m/%Y')
        ))
    
    print("\nLISTADO DE ENTIDADES REGISTRADAS")
    print_tabla(header, rows)

def menu_reportes_ingresos(db):
    while True:
        print("\nREPORTES DE INGRESOS")
        print("1. Reporte general de todos los ingresos")
        print("2. Reporte de ingresos por producto")
        print("3. Volver al menú principal")
        
        opcion = input("Seleccione una opción: ").strip()
        
        if opcion == '1':
            reporte_ingresos(db)
        elif opcion == '2':
            reporte_ingresos(db, por_producto=True)
        elif opcion == '3':
            break
        else:
            print("Opción inválida")

def menu_reportes_salidas(db):
    while True:
        print("\nREPORTES DE SALIDAS")
        print("1. Reporte general de todas las salidas")
        print("2. Reporte de salidas por producto")
        print("3. Volver al menú principal")
        
        opcion = input("Seleccione una opción: ").strip()
        
        if opcion == '1':
            reporte_salidas(db)
        elif opcion == '2':
            reporte_salidas(db, por_producto=True)
        elif opcion == '3':
            break
        else:
            print("Opción inválida")

def main():
    db = Database()
    while True:
        print("\n" + "=" * 40)
        print("SISTEMA DE GESTIÓN DE PRODUCTOS")
        print("=" * 40)
        print("1. Registrar unidad de medida")
        print("2. Listar unidades registradas")
        print("3. Registrar nuevo producto")
        print("4. Listar productos registrados")
        print("5. Registrar entidad (proveedor/usuario)")
        print("6. Listar entidades registradas")
        print("7. Registrar entrada de productos")
        print("8. Registrar salida de productos")
        print("9. Reporte general de movimientos")
        print("10. Reportes de ingresos")
        print("11. Reportes de salidas")
        print("12. Salir del sistema")
        
        opcion = input("\nSeleccione una opción: ").strip()
        
        if opcion == '1':
            registrar_unidades(db)
        elif opcion == '2':
            listar_unidades(db)
        elif opcion == '3':
            registrar_producto(db)
        elif opcion == '4':
            listar_productos(db)
        elif opcion == '5':
            registrar_entidad(db)
        elif opcion == '6':
            listar_entidades(db)
        elif opcion == '7':
            registrar_entrada(db)
        elif opcion == '8':
            registrar_salida(db)
        elif opcion == '9':
            reporte_movimientos(db)
        elif opcion == '10':
            menu_reportes_ingresos(db)
        elif opcion == '11':
            menu_reportes_salidas(db)
        elif opcion == '12':
            print("\n¡Gracias por usar el sistema!")
            break
        else:
            print("\nOpción inválida, intente nuevamente")
        
        input("\nPresione Enter para continuar...")

if __name__ == "__main__":
    main()
        