import os
import cv2
import re
import numpy as np
import time
import glob
from paddleocr import PaddleOCR

CARPETA_PENDIENTES = "pendientes"
CARPETA_RESULTADOS = "resultados"
CARPETA_PROCESADOS = "procesados"

for carpeta in [CARPETA_PENDIENTES, CARPETA_RESULTADOS, CARPETA_PROCESADOS]:
    os.makedirs(carpeta, exist_ok=True)

os.environ['FLAGS_enable_pir_api'] = '0'
os.environ['PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK'] = 'True'

print("Cargando motor PaddleOCR...")
ocr = PaddleOCR(lang='es', use_angle_cls=True, show_log=False)

ESTADOS_MEXICO = {
    "01": "AGUASCALIENTES", "02": "BAJA CALIFORNIA", "03": "BAJA CALIFORNIA SUR",
    "04": "CAMPECHE", "05": "COAHUILA", "06": "COLIMA", "07": "CHIAPAS",
    "08": "CHIHUAHUA", "09": "CDMX", "10": "DURANGO", "11": "GUANAJUATO",
    "12": "GUERRERO", "13": "HIDALGO", "14": "JALISCO", "15": "MEXICO",
    "16": "MICHOACAN", "17": "MORELOS", "18": "NAYARIT", "19": "NUEVO LEON",
    "20": "OAXACA", "21": "PUEBLA", "22": "QUERETARO", "23": "QUINTANA ROO",
    "24": "SAN LUIS POTOSI", "25": "SINALOA", "26": "SONORA", "27": "TABASCO",
    "28": "TAMAULIPAS", "29": "TLAXCALA", "30": "VERACRUZ", "31": "YUCATAN", "32": "ZACATECAS"
}

def validar_coherencia(curp, clave):
    if curp == "No encontrada" or clave == "No encontrada":
        return False
    return curp[4:10] == clave[6:12]

def extraer_datos_avanzado(resultado_ocr):

    res_curp = "No encontrada"
    res_clave = "No encontrada"
    res_nombre = "No encontrado"
    
    if not resultado_ocr or not resultado_ocr[0]:
        return res_curp, res_clave, res_nombre

    lineas_con_coordenadas = []
    y_nombre = None
    y_domicilio = None
    h_referencia = 15  
    
    
    for caja in resultado_ocr[0]:
        coordenadas = caja[0]
        texto = str(caja[1][0]).upper().strip()
        
        
        y_centro = sum([punto[1] for punto in coordenadas]) / 4
        x_centro = sum([punto[0] for punto in coordenadas]) / 4
        alto_texto = coordenadas[2][1] - coordenadas[0][1]
        
        lineas_con_coordenadas.append({
            "texto": texto, "x": x_centro, "y": y_centro, "h": alto_texto
        })
        
        t_limpio = texto.replace(" ", "")
        if re.search(r'[A-Z]{4}\d{6}[HM][A-Z5]{5}[A-Z\d]\d', t_limpio):
            res_curp = re.search(r'[A-Z]{4}\d{6}[HM][A-Z5]{5}[A-Z\d]\d', t_limpio).group()
        if re.search(r'[A-Z\d]{6}\d{6}[A-Z\d]{2}[A-Z\d]\d{3}', t_limpio):
            res_clave = re.search(r'[A-Z\d]{6}\d{6}[A-Z\d]{2}[A-Z\d]\d{3}', t_limpio).group()

        if "NOM" in texto and len(texto) <= 8 and y_nombre is None:
            y_nombre = y_centro
            h_referencia = alto_texto
        if "DOMICILIO" in texto and y_domicilio is None:
            y_domicilio = y_centro

    if y_nombre is not None:
        limite_inferior = y_domicilio if y_domicilio is not None else (y_nombre + (h_referencia * 5))
        candidatos_nombre = []
        
        for item in lineas_con_coordenadas:
            txt = item["texto"]
            y = item["y"]
            
            
            if y_nombre < y < limite_inferior:
                if re.search(r'\d', txt) or any(basura in txt for basura in ["NOMBRE", "DOMICILIO", "INSTITUTO", "NACIONAL"]):
                    continue
                
                txt_filtrado = re.sub(r'[^A-ZÑÁÉÍÓÚ ]', '', txt).strip()
                if len(txt_filtrado) > 2:
                    candidatos_nombre.append(item)
                    
        
        candidatos_nombre.sort(key=lambda item: item["y"])
        bloques_finales = [c["texto"] for c in candidatos_nombre[:3]]
        
        if bloques_finales:
            res_nombre = " ".join(bloques_finales)
            res_nombre = re.sub(r'\s+', ' ', res_nombre).strip()

    if res_nombre != "No encontrado" and res_curp != "No encontrada" and len(res_curp) >= 4:
        primera_letra_curp = res_curp[0]
        palabras_nombre = res_nombre.split(" ")
        
        if palabras_nombre and len(palabras_nombre[0]) > 3:
            primer_bloque = palabras_nombre[0]
            if not primer_bloque.startswith(primera_letra_curp):
                if primer_bloque[1:].startswith(primera_letra_curp):
                    palabras_nombre[0] = primer_bloque[1:]
                    res_nombre = " ".join(palabras_nombre)

    if res_nombre != "No encontrado":
        
        res_nombre = re.sub(r'[\.:,\-_]', '', res_nombre)
        res_nombre = re.sub(r'\s+', ' ', res_nombre).strip()
    

    return res_curp, res_clave, res_nombre

    


def procesar_ine(ruta_imagen):
    try:
        
        img_original = cv2.imread(ruta_imagen)
        if img_original is None:
            return {"error": "No se pudo leer la imagen"}

        
        gray = cv2.cvtColor(img_original, cv2.COLOR_BGR2GRAY)
        gray_large = cv2.resize(gray, None, fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
        blur = cv2.GaussianBlur(gray_large, (3,3), 0)
        _, img_limpia = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

        
        result = ocr.ocr(img_limpia, cls=True)
        
        
        curp, clave, nombre = extraer_datos_avanzado(result)
        
        
        if nombre == "No encontrado" or curp == "No encontrada":
            result_original = ocr.ocr(ruta_imagen, cls=True)
            c2, k2, n2 = extraer_datos_avanzado(result_original)
            if curp == "No encontrada": curp = c2
            if clave == "No encontrada": clave = k2
            if nombre == "No encontrado": nombre = n2

        coherente = validar_coherencia(curp, clave)
        anio_actual = 2026
        es_vigente = False
        anio_vencimiento = None
        
        textos_planos = []
        if result and result[0]:
            textos_planos = [linea[1][0] for linea in result[0]]
        texto_unido = " ".join(textos_planos).upper()
        
        anios_detectados = re.findall(r'20[23]\d', texto_unido)
        if anios_detectados:
            anios_int = [int(a) for a in anios_detectados]
            anio_vencimiento = max(anios_int)
            if anio_vencimiento >= anio_actual:
                es_vigente = True
        
        
        nombre_estado = "No identificado"
        if clave != "No encontrada" and len(clave) >= 14:
            cod_estado = clave[12:14]
            nombre_estado = ESTADOS_MEXICO.get(cod_estado, "Desconocido")

        
        gray_face = cv2.cvtColor(img_original, cv2.COLOR_BGR2GRAY)
        gray_face = cv2.equalizeHist(gray_face)
        face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        faces = face_cascade.detectMultiScale(gray_face, scaleFactor=1.05, minNeighbors=3, minSize=(40, 40))
        tiene_rostro = len(faces) > 0

        valido_final = (curp != "No encontrada" and tiene_rostro and es_vigente)

        return {
            "nombre": nombre,
            "curp": curp, 
            "clave_elector": clave, 
            "coinciden_fechas": coherente,
            "vigente": es_vigente, 
            "anio_vencimiento": anio_vencimiento,
            "estado": nombre_estado, 
            "tiene_rostro": tiene_rostro, 
            "valido": valido_final
        }
    except Exception as e:
        return {"error": str(e)}


if __name__ == "__main__":
    print(f"Monitor iniciado. Coloca imágenes en la carpeta '{CARPETA_PENDIENTES}'...")
    
    while True:
        archivos = glob.glob(os.path.join(CARPETA_PENDIENTES, "*.*"))
        
        for ruta_file in archivos:
            nombre_archivo = os.path.basename(ruta_file)
            print(f"Procesando identificación: {nombre_archivo}...")
            
            res = procesar_ine(ruta_file)
            
            nombre_resultado = os.path.splitext(nombre_archivo)[0] + "_resultado.txt"
            ruta_resultado = os.path.join(CARPETA_RESULTADOS, nombre_resultado)
            
            with open(ruta_resultado, "w", encoding="utf-8") as f:
                f.write(f"RESULTADO PARA: {nombre_archivo}\n")
                f.write("="*30 + "\n")
                for k, valor in res.items():
                    f.write(f"{k}: {valor}\n")
            
            ruta_destino = os.path.join(CARPETA_PROCESADOS, nombre_archivo)
            if os.path.exists(ruta_destino):
                ruta_destino = os.path.join(CARPETA_PROCESADOS, f"{int(time.time())}_{nombre_archivo}")
            
            try:
                os.rename(ruta_file, ruta_destino)
                print(f"Finalizado: {nombre_archivo}. Resultado guardado en {CARPETA_RESULTADOS}")
            except Exception as e:
                print(f"Error al mover archivo: {e}")

        time.sleep(2)