import os
import json
import base64
import cv2
import numpy as np
import requests
import face_recognition
import sqlite3
from flask import Flask, jsonify, render_template, request, send_from_directory, redirect, url_for, flash, session
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename
from datetime import timedelta

from reconocimiento import cargar_rostros, reconocer_desde_imagen, mejorar_imagen, reconocer_desde_imagen_fernandoi
from database import get_db_connection, init_db
import logging

app = Flask(__name__)
app.secret_key = "K@p1t4l_S3cr3t_K3y_2026" # En producción usar algo seguro
app.permanent_session_lifetime = timedelta(minutes=30) # Session timeout de 30 min

def get_client_ip():
    """Obtiene la IP real del dispositivo buscando en encabezados comunes de proxies."""
    # 1. X-Forwarded-For (el estándar para proxies, tomamos la primera IP)
    x_forwarded_for = request.headers.get('X-Forwarded-For')
    if x_forwarded_for:
        return x_forwarded_for.split(',')[0].strip()
    
    # 2. X-Real-IP (común en Nginx)
    x_real_ip = request.headers.get('X-Real-IP')
    if x_real_ip:
        return x_real_ip.strip()
    
    # 3. Fallback a la IP que ve Flask directamente
    return request.remote_addr

# ─── Configuración de Login ──────────────────────────────────────────────────
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'admin_login'

class User(UserMixin):
    def __init__(self, id, username):
        self.id = id
        self.username = username

@login_manager.user_loader
def load_user(user_id):
    conn = get_db_connection()
    user = conn.execute('SELECT * FROM users WHERE id = ?', (user_id,)).fetchone()
    conn.close()
    if user:
        return User(user['id'], user['username'])
    return None

DB_PATH = "caras_db"
ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png"}

# Cargar rostros locales al iniciar (Dashboard Principal)
rostros_conocidos, nombres = cargar_rostros(DB_PATH)

# ─── Datos Dinámicos en Memoria ───────────────────────────────────────────────
# Estructura: { slug: { 'rostros': [], 'nombres': [], 'empresa': '', 'api': '', 'jwt': '' } }
configuraciones_dinamicas = {}

def sync_active_configs():
    """Carga todas las configuraciones activas desde la DB y sincroniza rostros."""
    global configuraciones_dinamicas
    print("🔄 Sincronizando todas las configuraciones dinámicas desde la DB...")
    
    conn = get_db_connection()
    rows = conn.execute('SELECT * FROM settings WHERE activo = 1').fetchall()
    conn.close()
    
    for row in rows:
        slug = row['slug']
        url = row['api_url']
        jwt = row['jwt']
        empresa = row['empresa_nombre']
        url_timbrada = row['api_url_timbrada']
        url_pin = row['api_url_pin'] if row['api_url_pin'] else ''
        login_pin = 1 if row['login_pin'] else 0
        
        # Sincronizar rostros para este slug
        rostros, nombres = fetch_and_encode_faces(slug, url, jwt)
        
        configuraciones_dinamicas[slug] = {
            'rostros': rostros,
            'nombres': nombres,
            'empresa': empresa,
            'api_url': url,
            'api_url_timbrada': url_timbrada,
            'api_url_pin': url_pin,
            'login_pin': login_pin,
            'jwt': jwt
        }
    print(f"✨ Sincronización completada. {len(configuraciones_dinamicas)} slugs cargados.")

def fetch_and_encode_faces(slug, url, jwt):
    """Lógica genérica para obtener y codificar rostros desde un API."""
    rostros = []
    nombres = []
    print(f"  📥 Obteniendo rostros para '{slug}'...")
    try:
        headers = {"Authorization": f"Bearer {jwt}"}
        response = requests.get(url, headers=headers, timeout=30)
        
        if response.status_code != 200:
            print(f"  ❌ Error HTTP {response.status_code} al obtener rostros para '{slug}'")
            return [], []

        try:
            # Intentar limpiar posibles debugs de PHP (ej: string(30) "...") antes del JSON
            raw_text = response.text.strip()
            start_idx = raw_text.find('{')
            if start_idx != -1:
                raw_text = raw_text[start_idx:]
            
            data = json.loads(raw_text)
        except Exception as e:
            print(f"  ❌ Error parseando JSON para '{slug}': {e}")
            print(f"  🔍 Respuesta original: {response.text[:500]}")
            return [], []
        
        if not data.get("success"):
            print(f"  ❌ Error API '{slug}': {data.get('message')}")
            return [], []

        for colab in data.get("colaboradores", []):
            id_colab = colab.get("identificacion")
            foto_b64 = colab.get("foto_base64")
            
            if not id_colab or not foto_b64: continue
                
            try:
                img_data = base64.b64decode(foto_b64)
                np_arr = np.frombuffer(img_data, np.uint8)
                img_bgr = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
                if img_bgr is None: continue
                
                img_mejorada = mejorar_imagen(img_bgr)
                enc = face_recognition.face_encodings(img_mejorada)
                if not enc:
                    rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
                    enc = face_recognition.face_encodings(rgb)
                
                if enc:
                    rostros.append(enc[0])
                    nombres.append(id_colab)
            except Exception as e:
                print(f"  ⚠️ Error procesando {id_colab} en {slug}: {e}")
                
        print(f"  ✅ '{slug}' cargado con {len(nombres)} rostros.")
        return rostros, nombres
    except Exception as e:
        print(f"  ❌ Fallo crítico en fetch '{slug}': {e}")
        return [], []

# Inicializar DB y Cargar Configs
init_db()
sync_active_configs()


def allowed_file(filename):
    return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS


# ─── Página principal ────────────────────────────────────────────────────────
@app.route("/")
def index():
    return render_template("index.html")


# ─── Reconocimiento desde cámara (base64) ────────────────────────────────────
@app.route("/reconocer", methods=["POST"])
def reconocer():
    data = request.get_json()
    if not data or "imagen" not in data:
        return jsonify({"resultado": "❌ No se recibió imagen"}), 400

    try:
        header, encoded = data["imagen"].split(",", 1)
        imagen_bytes = base64.b64decode(encoded)
    except Exception:
        return jsonify({"resultado": "❌ Formato de imagen inválido"}), 400

    nombre, mensaje = reconocer_desde_imagen(rostros_conocidos, nombres, imagen_bytes)
    return jsonify({"resultado": mensaje, "nombre": nombre})


# ─── Guardar foto tomada desde la cámara (base64) ───────────────────────────
@app.route("/capturar-cara", methods=["POST"])
def capturar_cara():
    global rostros_conocidos, nombres

    data = request.get_json()
    nombre_persona = (data.get("nombre") or "").strip()
    imagen_b64 = data.get("imagen", "")

    if not nombre_persona:
        return jsonify({"ok": False, "mensaje": "❌ Debes ingresar un nombre"}), 400
    if not imagen_b64:
        return jsonify({"ok": False, "mensaje": "❌ No se recibió imagen"}), 400

    try:
        header, encoded = imagen_b64.split(",", 1)
        imagen_bytes = base64.b64decode(encoded)
    except Exception:
        return jsonify({"ok": False, "mensaje": "❌ Formato de imagen inválido"}), 400

    nombre_archivo = secure_filename(f"{nombre_persona}.jpg")
    ruta_destino = os.path.join(DB_PATH, nombre_archivo)

    with open(ruta_destino, "wb") as f:
        f.write(imagen_bytes)

    rostros_conocidos, nombres = cargar_rostros(DB_PATH)
    return jsonify({
        "ok": True,
        "mensaje": f"✅ '{nombre_persona}' guardado correctamente ({len(nombres)} rostro(s) en total)"
    })


# ─── Subir imagen a caras_db ─────────────────────────────────────────────────
@app.route("/subir-cara", methods=["POST"])
def subir_cara():
    global rostros_conocidos, nombres

    if "imagen" not in request.files:
        return jsonify({"ok": False, "mensaje": "❌ No se envió ningún archivo"}), 400

    archivo = request.files["imagen"]
    nombre_persona = request.form.get("nombre", "").strip()

    if not nombre_persona:
        return jsonify({"ok": False, "mensaje": "❌ Debes ingresar un nombre"}), 400

    if archivo.filename == "" or not allowed_file(archivo.filename):
        return jsonify({"ok": False, "mensaje": "❌ Formato inválido. Usa JPG o PNG"}), 400

    ext = archivo.filename.rsplit(".", 1)[1].lower()
    nombre_archivo = secure_filename(f"{nombre_persona}.{ext}")
    ruta_destino = os.path.join(DB_PATH, nombre_archivo)

    archivo.save(ruta_destino)

    # Recargar la base de rostros automáticamente
    rostros_conocidos, nombres = cargar_rostros(DB_PATH)

    return jsonify({
        "ok": True,
        "mensaje": f"✅ '{nombre_persona}' guardado correctamente ({len(nombres)} rostro(s) en total)"
    })


# ─── Listar rostros registrados ───────────────────────────────────────────────
@app.route("/listar-caras")
def listar_caras():
    archivos = [
        f for f in os.listdir(DB_PATH)
        if f.lower().endswith((".jpg", ".jpeg", ".png"))
    ]
    return jsonify({"caras": archivos, "total": len(archivos)})


# ─── Servir imagen de caras_db ────────────────────────────────────────────────
@app.route("/ver-cara/<path:filename>")
def ver_cara(filename):
    return send_from_directory(DB_PATH, filename)


# ─── Eliminar un rostro ────────────────────────────────────────────────────────
@app.route("/eliminar-cara", methods=["POST"])
def eliminar_cara():
    global rostros_conocidos, nombres

    data = request.get_json()
    archivo = data.get("archivo", "")
    ruta = os.path.join(DB_PATH, secure_filename(archivo))

    if os.path.exists(ruta):
        os.remove(ruta)
        rostros_conocidos, nombres = cargar_rostros(DB_PATH)
        return jsonify({"ok": True, "mensaje": f"🗑️ '{archivo}' eliminado"})

    return jsonify({"ok": False, "mensaje": "❌ Archivo no encontrado"}), 404


# ─── ADMINISTRACIÓN ──────────────────────────────────────────────────────────

@app.route("/admin/login", methods=["GET", "POST"])
def admin_login():
    if current_user.is_authenticated:
        return redirect(url_for('admin_dashboard'))
    
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        
        conn = get_db_connection()
        user = conn.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone()
        conn.close()
        
        if user and check_password_hash(user['password_hash'], password):
            user_obj = User(user['id'], user['username'])
            login_user(user_obj, remember=True)
            return redirect(url_for('admin_dashboard'))
        else:
            flash("❌ Usuario o contraseña incorrectos", "error")
            
    return render_template("admin_login.html")

@app.route("/admin/logout")
@login_required
def admin_logout():
    logout_user()
    return redirect(url_for('admin_login'))

@app.route("/admin/dashboard")
@login_required
def admin_dashboard():
    conn = get_db_connection()
    rows = conn.execute('SELECT * FROM settings').fetchall()
    conn.close()
    # Convertimos filas de SQLite a diccionarios para que sean serializables (tojson)
    configs = [dict(row) for row in rows]
    return render_template("admin_dashboard.html", configs=configs, configuraciones_dinamicas=configuraciones_dinamicas)

@app.route("/admin/save-config", methods=["POST"])
@login_required
def admin_save_config():
    conf_id = request.form.get("id")
    slug = request.form.get("slug").strip().lower()
    api_url = request.form.get("api_url").strip()
    jwt = request.form.get("jwt").strip()
    empresa = request.form.get("empresa").strip()
    api_url_timbrada = request.form.get("api_url_timbrada", "").strip()
    api_url_pin = request.form.get("api_url_pin", "").strip()
    login_pin = 1 if request.form.get("login_pin") == "on" else 0
    
    conn = get_db_connection()
    try:
        if conf_id: # Update
            conn.execute('''
                UPDATE settings SET slug=?, api_url=?, api_url_timbrada=?, api_url_pin=?, login_pin=?, jwt=?, empresa_nombre=? WHERE id=?
            ''', (slug, api_url, api_url_timbrada, api_url_pin, login_pin, jwt, empresa, conf_id))
        else: # Insert
            conn.execute('''
                INSERT INTO settings (slug, api_url, api_url_timbrada, api_url_pin, login_pin, jwt, empresa_nombre) VALUES (?, ?, ?, ?, ?, ?, ?)
            ''', (slug, api_url, api_url_timbrada, api_url_pin, login_pin, jwt, empresa))
        conn.commit()
        flash("✅ Configuración guardada correctamente", "success")
        # Sincronizar este slug inmediatamente
        rostros, nombres_api = fetch_and_encode_faces(slug, api_url, jwt)
        configuraciones_dinamicas[slug] = {
            'rostros': rostros,
            'nombres': nombres_api,
            'empresa': empresa,
            'api_url': api_url,
            'api_url_timbrada': api_url_timbrada,
            'api_url_pin': api_url_pin,
            'login_pin': login_pin,
            'jwt': jwt
        }
    except sqlite3.IntegrityError:
        flash(f"❌ El slug '{slug}' ya existe", "error")
    except Exception as e:
        flash(f"❌ Error: {str(e)}", "error")
    finally:
        conn.close()
        
    return redirect(url_for('admin_dashboard'))

@app.route("/admin/delete-config/<int:id>", methods=["POST"])
@login_required
def admin_delete_config(id):
    conn = get_db_connection()
    row = conn.execute('SELECT slug FROM settings WHERE id = ?', (id,)).fetchone()
    if row:
        slug = row['slug']
        conn.execute('DELETE FROM settings WHERE id = ?', (id,))
        conn.commit()
        if slug in configuraciones_dinamicas:
            del configuraciones_dinamicas[slug]
        flash(f"🗑️ Configuración '{slug}' eliminada", "success")
    conn.close()
    return redirect(url_for('admin_dashboard'))




@app.route("/admin/users", methods=["GET", "POST"])
@login_required
def admin_users():
    conn = get_db_connection()
    if request.method == "POST":
        data = request.get_json()
        slug = data.get('slug')
        username = data.get('username')
        password = data.get('password')
        role = data.get('role')
        
        if not all([slug, username, password, role]):
            return jsonify({"success": False, "message": "Faltan campos"}), 400
            
        # Validación de usuario repetido en el slug
        existing = conn.execute(
            'SELECT * FROM slug_users WHERE slug = ? AND username = ?', (slug, username)
        ).fetchone()
        if existing:
            conn.close()
            return jsonify({"success": False, "message": f"El usuario '{username}' ya existe para el slug '{slug}'"}), 400

        h = generate_password_hash(password)
        try:
            conn.execute('''
                INSERT INTO slug_users (slug, username, password_hash, role) VALUES (?, ?, ?, ?)
            ''', (slug, username, h, role))
            conn.commit()
            return jsonify({"success": True})
        except Exception as e:
            return jsonify({"success": False, "message": str(e)}), 500
        finally:
            conn.close()
            
    users = conn.execute('SELECT * FROM slug_users').fetchall()
    conn.close()
    return jsonify([dict(u) for u in users])

@app.route("/admin/change-password", methods=["POST"])
@login_required
def admin_change_password():
    data = request.get_json()
    new_password = data.get('password')
    if not new_password:
        return jsonify({"success": False, "message": "Contraseña vacía"}), 400
    
    h = generate_password_hash(new_password)
    conn = get_db_connection()
    conn.execute('UPDATE users SET password_hash = ? WHERE username = ?', (h, current_user.username))
    conn.commit()
    conn.close()
    return jsonify({"success": True})

@app.route("/<slug>/change-password", methods=["POST"])
def slug_change_password(slug):
    if 'slug_user' not in session or session['slug_user']['slug'] != slug:
        return jsonify({"success": False, "message": "No autorizado"}), 403
        
    data = request.get_json()
    new_password = data.get('password')
    if not new_password:
        return jsonify({"success": False, "message": "Contraseña vacía"}), 400
        
    h = generate_password_hash(new_password)
    conn = get_db_connection()
    conn.execute(
        'UPDATE slug_users SET password_hash = ? WHERE id = ? AND slug = ?', 
        (h, session['slug_user']['id'], slug)
    )
    conn.commit()
    conn.close()
    return jsonify({"success": True})

@app.route("/admin/delete-user/<int:id>", methods=["POST"])
@login_required
def admin_delete_user(id):
    conn = get_db_connection()
    conn.execute('DELETE FROM slug_users WHERE id = ?', (id,))
    conn.commit()
    conn.close()
    return jsonify({"success": True})


# ─── RUTA DINÁMICA ─────────────────────────────────────────────────────────────

@app.route("/<slug>")
def dynamic_reconocedor(slug):
    # Validar IP
    ip_address = get_client_ip()

    conn = get_db_connection()
    allowed = conn.execute(
        'SELECT status FROM allowed_ips WHERE slug = ? AND ip_address = ?', (slug, ip_address)
    ).fetchone()
    conn.close()

    if not allowed or allowed['status'] != 'approved':
        return redirect(url_for('slug_ip_request', slug=slug))

    if slug not in configuraciones_dinamicas:
        # Si no está en memoria, intentamos buscar en DB e inicializarlo
        conn = get_db_connection()
        row = conn.execute('SELECT * FROM settings WHERE slug = ? AND activo = 1', (slug,)).fetchone()
        conn.close()
        if row:
            rostros, nombres_api = fetch_and_encode_faces(row['slug'], row['api_url'], row['jwt'])
            configuraciones_dinamicas[row['slug']] = {
                'rostros': rostros, 'nombres': nombres_api, 'empresa': row['empresa_nombre'],
                'api_url': row['api_url'], 'api_url_timbrada': row['api_url_timbrada'],
                'api_url_pin': row['api_url_pin'] if row['api_url_pin'] else '',
                'login_pin': 1 if row['login_pin'] else 0,
                'jwt': row['jwt']
            }
        else:
            return "❌ Ruta no encontrada o inactiva", 404
            
    config = configuraciones_dinamicas[slug]
    return render_template("reconocedor_dinamico.html", slug=slug, empresa=config['empresa'], login_pin=config.get('login_pin', 0))

@app.route("/<slug>/ip", methods=["GET", "POST"])
def slug_ip_request(slug):
    # Obtener IP del cliente (considerando proxies como Docker/Nginx)
    ip_address = get_client_ip()

    if request.method == "POST":
        conn = get_db_connection()
        try:
            # Verificar si ya existe
            existing = conn.execute(
                'SELECT * FROM allowed_ips WHERE slug = ? AND ip_address = ?', (slug, ip_address)
            ).fetchone()
            
            if existing:
                msg = "Tu IP ya está registrada (Estado: " + existing['status'] + ")"
                return jsonify({"success": True if existing['status'] == 'approved' else False, "message": msg})
            
            conn.execute(
                'INSERT INTO allowed_ips (slug, ip_address, status) VALUES (?, ?, ?)', (slug, ip_address, 'pending')
            )
            conn.commit()
            return jsonify({"success": True, "message": "Solicitud enviada. Espera aprobación del administrador."})
        except Exception as e:
            return jsonify({"success": False, "message": str(e)}), 500
        finally:
            conn.close()

    return render_template("slug_ip_request.html", slug=slug, ip=ip_address)

@app.route("/<slug>/login", methods=["GET", "POST"])
def slug_login(slug):
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        
        conn = get_db_connection()
        user = conn.execute(
            'SELECT * FROM slug_users WHERE slug = ? AND username = ?', (slug, username)
        ).fetchone()
        conn.close()
        
        if user and check_password_hash(user['password_hash'], password):
            session['slug_user'] = {
                'id': user['id'],
                'username': user['username'],
                'slug': user['slug'],
                'role': user['role']
            }
            return redirect(url_for('slug_home', slug=slug))
        else:
            return render_template("slug_login.html", slug=slug, error="Usuario o contraseña incorrectos")

    return render_template("slug_login.html", slug=slug)

@app.route("/<slug>/logout")
def slug_logout(slug):
    if 'slug_user' in session and session['slug_user']['slug'] == slug:
        session.pop('slug_user', None)
    return redirect(url_for('slug_login', slug=slug))

@app.route("/<slug>/home")
def slug_home(slug):
    if 'slug_user' not in session or session['slug_user']['slug'] != slug:
        return redirect(url_for('slug_login', slug=slug))
    return render_template("slug_home.html", slug=slug, user=session['slug_user'])

@app.route("/<slug>/ips-list")
def slug_ips_list(slug):
    if 'slug_user' not in session or session['slug_user']['slug'] != slug or session['slug_user']['role'] != 'admin':
        return jsonify([]), 403
    conn = get_db_connection()
    ips = conn.execute('SELECT * FROM allowed_ips WHERE slug = ?', (slug,)).fetchall()
    conn.close()
    return jsonify([dict(i) for i in ips])

@app.route("/<slug>/approve-ip/<int:ip_id>", methods=["POST"])
def slug_approve_ip(slug, ip_id):
    if 'slug_user' not in session or session['slug_user']['slug'] != slug or session['slug_user']['role'] != 'admin':
        return jsonify({"success": False}), 403
    conn = get_db_connection()
    conn.execute('UPDATE allowed_ips SET status = ? WHERE id = ? AND slug = ?', ('approved', ip_id, slug))
    conn.commit()
    conn.close()
    return jsonify({"success": True})

@app.route("/<slug>/delete-ip/<int:ip_id>", methods=["POST"])
def slug_delete_ip(slug, ip_id):
    if 'slug_user' not in session or session['slug_user']['slug'] != slug or session['slug_user']['role'] != 'admin':
        return jsonify({"success": False}), 403
    conn = get_db_connection()
    conn.execute('DELETE FROM allowed_ips WHERE id = ? AND slug = ?', (ip_id, slug))
    conn.commit()
    conn.close()
    return jsonify({"success": True})

@app.route("/reconocer-dinamico/<slug>", methods=["POST"])
def reconocer_dinamico_api(slug):
    # Sincronización bajo demanda (según pedido del usuario)
    # Buscamos la configuración en la DB para cada reconocimiento
    conn = get_db_connection()
    row = conn.execute('SELECT * FROM settings WHERE slug = ? AND activo = 1', (slug,)).fetchone()
    conn.close()
    
    if row:
        # Actualizamos la memoria con los datos más recientes del API antes de reconocer
        rostros, nombres_api = fetch_and_encode_faces(row['slug'], row['api_url'], row['jwt'])
        configuraciones_dinamicas[row['slug']] = {
            'rostros': rostros, 'nombres': nombres_api, 'empresa': row['empresa_nombre'],
            'api_url': row['api_url'], 'api_url_timbrada': row['api_url_timbrada'],
            'api_url_pin': row['api_url_pin'] if row['api_url_pin'] else '',
            'login_pin': 1 if row['login_pin'] else 0,
            'jwt': row['jwt']
        }
    else:
        if slug not in configuraciones_dinamicas:
            return jsonify({"resultado": "❌ Sesión de reconocimiento no encontrada"}), 404
        
    data = request.get_json()
    if not data or "imagen" not in data:
        return jsonify({"resultado": "❌ No se recibió imagen"}), 400
        
    target_id = data.get("target_id") # <-- Recuperamos target_id si se provee

    try:
        header, encoded = data["imagen"].split(",", 1)
        imagen_bytes = base64.b64decode(encoded)
    except Exception:
        return jsonify({"resultado": "❌ Formato de imagen inválido"}), 400

    ip_dispositivo = get_client_ip()
    config = configuraciones_dinamicas[slug]
    # Pasamos target_id para que valide estrictamente si existe
    nombre_res, mensaje_res, match_procentaje = reconocer_desde_imagen_fernandoi(
        config['rostros'], config['nombres'], imagen_bytes, target_id=target_id
    )
    login_pin = config.get('login_pin', 0)

    # ─── Flujo login_pin=1: solo devuelve el match, NO timbra ────────────────
    if login_pin:
        return jsonify({"resultado": mensaje_res, "nombre": nombre_res, "login_pin": 1, "match_procentaje": match_procentaje})

    # ─── Flujo normal: Registro de Timbrada Automático (SI HAY MATCH) ─────────
    id_confirmado = None
    if nombre_res: # nombre_res contiene la 'identificacion'
        id_confirmado = nombre_res
        api_timbrada = config.get('api_url_timbrada')
        
        if api_timbrada:
            try:
                from datetime import datetime
                ahora = datetime.now()
                payload = {
                    "identificacion": id_confirmado,
                    "fecha": ahora.strftime("%Y-%m-%d"),
                    "hora": ahora.strftime("%H:%M:%S"),
                    "base64IMG": encoded, # Usamos la imagen capturada (sin header)
                    "ip_dispositivo": ip_dispositivo,
                    "match_procentaje": match_procentaje
                }
                headers = {"Authorization": f"Bearer {config['jwt']}"}
                resp_t = requests.post(api_timbrada, json=payload, headers=headers, timeout=10)
                result_t = resp_t.json()
                
                if result_t.get("success"):
                    mensaje_res += " | ✅ Timbrada registrada"
                else:
                    mensaje_res += f" | ⚠️ Error Timbrada: {result_t.get('message')}"
            except Exception as e:
                mensaje_res += f" | ❌ Fallo Timbrada: {str(e)}"

    return jsonify({"resultado": mensaje_res, "nombre": nombre_res, "login_pin": 0, "match_procentaje": match_procentaje})


# ─── Verificar PIN (proxy Flask → API externa, sin CORS) ─────────────────────
@app.route("/verificar-pin/<slug>", methods=["POST"])
def verificar_pin(slug):
    conn = get_db_connection()
    try:
        row = conn.execute(
            'SELECT api_url_pin, api_url, jwt FROM settings WHERE slug = ? AND activo = 1', (slug,)
        ).fetchone()
    except Exception:
        # Columna faltante en DB antigua → migrar al vuelo y reintentar
        try:
            conn.execute('ALTER TABLE settings ADD COLUMN api_url_pin TEXT')
            conn.commit()
        except Exception:
            pass
        row = conn.execute(
            'SELECT api_url_pin, api_url, jwt FROM settings WHERE slug = ? AND activo = 1', (slug,)
        ).fetchone()
    finally:
        conn.close()

    if not row:
        return jsonify({"success": False, "message": "Slug no encontrado"}), 404

    api_pin       = row['api_url_pin']
    api_url_colab = row['api_url']   # endpoint colaboradores_activos
    jwt_token     = row['jwt']

    if not api_pin:
        return jsonify({"success": False, "message": "API URL (PIN) no configurada en el admin"}), 400

    data = request.get_json()
    pin  = data.get('pin', '')
    cedula = data.get('cedula', '')

    ip_dispositivo = get_client_ip()
    print(f"🔐 Verificando PIN '{slug}' (IP: {ip_dispositivo}) → {api_pin}")
    try:
        headers = {"Authorization": f"Bearer {jwt_token}"}
        resp = requests.post(api_pin, json={"pin": pin, "cedula": cedula, "ip_dispositivo": ip_dispositivo}, headers=headers, timeout=10)
        print(f"  HTTP {resp.status_code} | body: {resp.text[:300]}")
        try:
            result = resp.json()
        except Exception:
            msg = f"HTTP {resp.status_code}"
            if resp.status_code == 404:
                msg = f"404 – URL no encontrada: {api_pin}"
            else:
                msg = f"HTTP {resp.status_code} – respuesta inválida: {resp.text[:150]}"
            return jsonify({"success": False, "message": msg}), 500
        print(f"  PIN result: success={result.get('success')}")

        # ── Si el PIN es válido, enriquecer con datos de jornada del colaborador ──
        if result.get("success") and api_url_colab:
            try:
                colab_resp = requests.get(api_url_colab, headers=headers, timeout=15)
                if colab_resp.status_code == 200:
                    raw = colab_resp.text.strip()
                    start_idx = raw.find('{')
                    if start_idx != -1:
                        raw = raw[start_idx:]
                    colab_data = json.loads(raw)
                    colaboradores = colab_data.get("colaboradores", [])
                    # Buscar el colaborador cuya identificacion coincida con la cédula
                    colab_info = next(
                        (c for c in colaboradores if str(c.get("identificacion", "")) == str(cedula)),
                        None
                    )
                    if colab_info:
                        result["almuerzo"]              = colab_info.get("almuerzo", "0")
                        result["total_registros_horas"] = colab_info.get("total_registros_horas", 0)
                        result["id_jornada"]            = colab_info.get("id_jornada", None)
                        result["dias"]                  = colab_info.get("dias", {})
                        print(f"  🍽️  almuerzo={result['almuerzo']} | total_registros_horas={result['total_registros_horas']} | dias={result['dias']}")
                    else:
                        print(f"  ⚠️  Colaborador '{cedula}' no encontrado en colaboradores_activos")
            except Exception as e:
                print(f"  ⚠️  No se pudo obtener jornada desde colaboradores_activos: {e}")

        return jsonify(result)
    except requests.exceptions.RequestException as e:
        print(f"  ❌ Error de red PIN: {e}")
        return jsonify({"success": False, "message": f"Error de red: {str(e)}"}), 500


# ─── Timbrar Manual (flujo login_pin) ────────────────────────────────────────
@app.route("/timbrar-manual/<slug>", methods=["POST"])
def timbrar_manual(slug):
    if slug not in configuraciones_dinamicas:
        return jsonify({"success": False, "message": "Slug no encontrado"}), 404

    config = configuraciones_dinamicas[slug]
    api_timbrada = config.get('api_url_timbrada')
    if not api_timbrada:
        return jsonify({"success": False, "message": "API Timbrada no configurada"}), 400

    data = request.get_json()
    identificacion = data.get('identificacion', '')
    fecha_cliente = data.get('fecha', '')
    hora_cliente  = data.get('hora', '')
    encoded_img = data.get('base64IMG', '')
    match_procentaje = data.get('match_procentaje', None)

    try:
        from datetime import datetime
        ahora = datetime.now()
        fecha_final = fecha_cliente if fecha_cliente else ahora.strftime("%Y-%m-%d")
        hora_final  = hora_cliente if hora_cliente else ahora.strftime("%H:%M:%S")

        payload = {
            "identificacion": identificacion,
            "fecha": fecha_final,
            "hora": hora_final,
            "base64IMG": encoded_img,
            "ip_dispositivo": get_client_ip(),
            "match_procentaje": match_procentaje
        }
        headers = {"Authorization": f"Bearer {config['jwt']}"}

        # ── LOG: request saliente ──────────────────────────────────────────────
        print(f"📤 [timbrar-manual/{slug}] → POST {api_timbrada}")
        print(f"   identificacion  : {identificacion}")
        print(f"   fecha           : {fecha_final}  |  hora: {hora_final}")
        print(f"   ip_dispositivo  : {get_client_ip()}")
        print(f"   match_procentaje: {match_procentaje}")
        print(f"   base64IMG       : {'(presente, ' + str(len(encoded_img)) + ' chars)' if encoded_img else '(vacío)'}")

        resp = requests.post(api_timbrada, json=payload, headers=headers, timeout=10)

        # ── LOG: respuesta recibida ────────────────────────────────────────────
        print(f"📥 [timbrar-manual/{slug}] ← HTTP {resp.status_code}")
        print(f"   Body: {resp.text[:500]}")

        try:
            result = resp.json()
            print(f"   JSON parsed → success={result.get('success')} | message={result.get('message')}")
        except Exception:
            print(f"   ⚠️  Respuesta no es JSON válido")
            return jsonify({"success": False, "message": f"HTTP {resp.status_code} – respuesta inválida: {resp.text[:150]}"}), 500

        return jsonify(result)
    except Exception as e:
        print(f"❌ [timbrar-manual/{slug}] Excepción: {e}")
        return jsonify({"success": False, "message": str(e)}), 500


# ─── API: Config pública del slug (leída directo de SQLite) ──────────────────
@app.route("/api/slug-config/<slug>")
def api_slug_config(slug):
    conn = get_db_connection()
    row = conn.execute(
        'SELECT login_pin, api_url_pin, jwt FROM settings WHERE slug = ? AND activo = 1', (slug,)
    ).fetchone()
    conn.close()
    if row:
        return jsonify({
            "login_pin":  int(row['login_pin'])  if row['login_pin']  else 0,
            "api_url_pin": row['api_url_pin'] or "",
            "jwt": row['jwt'] or ""
        })
    return jsonify({"login_pin": 0, "api_url_pin": "", "jwt": ""})


@app.route("/debug-ip")
def debug_ip():
    """Ruta para diagnosticar qué IP y encabezados está recibiendo el servidor."""
    return jsonify({
        "remote_addr": request.remote_addr,
        "X-Forwarded-For": request.headers.get('X-Forwarded-For'),
        "X-Real-IP": request.headers.get('X-Real-IP'),
        "all_headers": dict(request.headers),
        "detected_ip": get_client_ip()
    })


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8010, debug=True)