"""Interfaz web para el ingreso automático de licencias médicas.

Ejecutar:
    python web.py
Luego abrir en el navegador:  http://localhost:5000
"""
import hmac
import os
import threading
import traceback
from functools import wraps
from datetime import datetime
from flask import (Flask, jsonify, request, render_template_string,
                   send_from_directory, session, redirect, url_for)
from werkzeug.utils import secure_filename

from flask import send_file
from config import (EXCEL_PATH, APP_USUARIO, APP_CLAVE, APP_SECRET_KEY)
from excel_reader import leer_licencias
from automation import LicenciaBot, LicenciaError, CAPTURAS_DIR, DOCUMENTOS_DIR
from remuneraciones import escribir_en_excel, escribir_comentario
import historial

ENTRADAS_DIR = "entradas"

# Marca del código cargado: permite detectar un servidor viejo aún corriendo
# (Python importa los módulos al arrancar; si editas el código hay que reiniciar).
def _version_codigo():
    import automation as _a
    ts = os.path.getmtime(_a.__file__)
    return datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S")


app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 20 * 1024 * 1024  # 20 MB
app.secret_key = APP_SECRET_KEY


def login_required(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if not session.get("autenticado"):
            if request.path.startswith(("/estado", "/procesar", "/subir", "/limpiar")):
                return jsonify({"ok": False, "msg": "Sesión expirada. Vuelve a iniciar sesión."}), 401
            return redirect(url_for("login"))
        return f(*args, **kwargs)
    return wrapper


@app.route("/login", methods=["GET", "POST"])
def login():
    error = None
    if request.method == "POST":
        u = request.form.get("usuario", "")
        c = request.form.get("clave", "")
        if hmac.compare_digest(u, APP_USUARIO) and hmac.compare_digest(c, APP_CLAVE):
            session["autenticado"] = True
            session["usuario"] = u
            return redirect(url_for("index"))
        error = "Usuario o clave incorrectos."
    return render_template_string(PLANTILLA_LOGIN, error=error)


@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("login"))

# Estado global compartido con el hilo de trabajo
estado = {
    "corriendo": False,
    "modo": None,          # "prueba" | "envio"
    "log": [],
    "resultados": [],
    "terminado": False,
    "inicio": None,
    "excel": EXCEL_PATH,   # Excel seleccionado actualmente
    "total": 0,            # licencias a procesar
    "progreso": 0,         # 0-100
    "paso": "",            # descripción del paso actual
    "version_codigo": _version_codigo(),
}
_lock = threading.Lock()


def log(msg):
    estado["log"].append(f"[{datetime.now():%H:%M:%S}] {msg}")


def _comentar(excel_path, fila, texto):
    """Escribe el comentario/resultado en el Excel, sin romper el proceso si falla."""
    try:
        escribir_comentario(excel_path, fila, texto)
    except Exception as e:
        log(f"Fila {fila}: no pude escribir el comentario en el Excel: {e}")


def worker(enviar, fila_filtro, headed, excel_path):
    try:
        licencias = leer_licencias(excel_path)
        if fila_filtro:
            licencias = [l for l in licencias if l.get("_fila_excel") == fila_filtro]
        total = len(licencias) or 1
        estado["total"] = len(licencias)
        log(f"Excel: {excel_path}")
        log(f"{len(licencias)} licencia(s) a procesar. Modo: "
            f"{'ENVÍO REAL' if enviar else 'PRUEBA (no guarda)'}")
        with LicenciaBot(headless=not headed) as bot:
            estado["paso"] = "Iniciando sesión en el sistema..."
            log("Iniciando sesión en el sistema...")
            bot.login()
            log("Sesión iniciada correctamente.")
            for i, lic in enumerate(licencias):
                fila = lic.get("_fila_excel")
                log(f"Fila {fila} · RUT {lic['rut']} · Lic {lic['num_licencia']} — procesando...")

                def cb(frac, texto, _i=i, _fila=fila):
                    estado["progreso"] = min(100, int((_i + frac) / total * 100))
                    estado["paso"] = f"Licencia {_i + 1}/{total} (fila {_fila}): {texto}"

                try:
                    r = bot.ingresar_licencia(lic, enviar=enviar, on_progress=cb)
                    r["estado"] = "OK"
                    # Escribir remuneraciones en el Excel (solo envío real con datos)
                    remun = r.get("remuneraciones")
                    if enviar and r.get("pdf_cobro_sil") and remun and remun.get("meses"):
                        try:
                            escribir_en_excel(excel_path, r["fila_excel"], remun,
                                              os.path.basename(r["pdf_cobro_sil"]))
                            r["remuneraciones_escritas"] = True
                            log(f"Fila {fila}: remuneraciones escritas en el Excel")
                        except Exception as e:
                            log(f"Fila {fila}: no pude escribir remuneraciones: {e}")
                    # Registrar en el historial (solo licencias realmente guardadas con PDF)
                    if enviar and r.get("pdf_cobro_sil"):
                        try:
                            historial.agregar({
                                "rut": r.get("rut"),
                                "nombre": r.get("nombre_cargado"),
                                "num_licencia": r.get("num_licencia"),
                                "pdf": os.path.basename(r["pdf_cobro_sil"]),
                                "remuneraciones": remun,
                                "excel_origen": os.path.basename(excel_path),
                            })
                        except Exception as e:
                            log(f"Fila {fila}: no pude guardar en historial: {e}")
                    estado["resultados"].append(r)
                    estado["progreso"] = int((i + 1) / total * 100)
                    log(f"Fila {fila}: OK [{r['modo']}] — {r['nombre_cargado']}")
                    if r.get("pdf_cobro_sil"):
                        log(f"Fila {fila}: Cobro SIL -> {r['pdf_cobro_sil']}")
                    if r.get("error_cobro_sil"):
                        log(f"Fila {fila}: Cobro SIL ERROR: {r['error_cobro_sil']}")
                    if r.get("dialogos"):
                        log(f"Fila {fila}: mensajes del sistema: {r['dialogos']}")
                    # Comentario en el Excel
                    if not enviar:
                        coment = "Prueba OK (campos válidos, no guardada)"
                    elif r.get("pdf_cobro_sil"):
                        coment = "Guardada · Cobro SIL OK"
                    elif r.get("enviado"):
                        coment = f"Guardada (sin Cobro SIL: {r.get('error_cobro_sil','')})"
                    else:
                        coment = "Revisar: no se confirmó el guardado"
                    _comentar(excel_path, fila, coment)
                except LicenciaError as e:
                    estado["resultados"].append(
                        {"fila_excel": fila, "rut": lic["rut"], "estado": "ERROR", "error": str(e)})
                    log(f"Fila {fila}: ERROR — {e}")
                    _comentar(excel_path, fila, f"ERROR: {e}")
                except Exception as e:
                    estado["resultados"].append(
                        {"fila_excel": fila, "rut": lic["rut"], "estado": "ERROR", "error": str(e)})
                    log(f"Fila {fila}: ERROR inesperado — {e}")
                    _comentar(excel_path, fila, f"ERROR: {e}")
        estado["progreso"] = 100
        estado["paso"] = "Proceso finalizado."
        log("Proceso finalizado.")
    except Exception as e:
        estado["paso"] = f"Error: {e}"
        log(f"FALLO GENERAL: {e}")
        log(traceback.format_exc())
    finally:
        estado["corriendo"] = False
        estado["terminado"] = True


def _leer_seguro(ruta):
    """Devuelve (licencias, error) sin lanzar excepción."""
    try:
        return leer_licencias(ruta), None
    except Exception as e:
        return [], str(e)


@app.route("/")
@login_required
def index():
    # Al refrescar la página, la tabla arranca limpia (salvo que haya un proceso
    # en curso, para no perder la vista de lo que se está ejecutando).
    if not estado["corriendo"]:
        estado.update({"excel": "", "resultados": [], "log": [], "terminado": False,
                       "total": 0, "progreso": 0, "paso": ""})
    if not estado["excel"]:
        return render_template_string(PLANTILLA, licencias=[], excel="(ninguno)",
                                      error_excel=None)
    licencias, error_excel = _leer_seguro(estado["excel"])
    return render_template_string(PLANTILLA, licencias=licencias,
                                  excel=estado["excel"], error_excel=error_excel)


@app.route("/subir", methods=["POST"])
@login_required
def subir():
    """Recibe un .xlsx, lo guarda y lo deja como Excel seleccionado. Devuelve la vista previa."""
    if estado["corriendo"]:
        return jsonify({"ok": False, "msg": "Hay un proceso en curso; espera a que termine."}), 409
    if "archivo" not in request.files or request.files["archivo"].filename == "":
        return jsonify({"ok": False, "msg": "No se recibió ningún archivo."}), 400
    f = request.files["archivo"]
    nombre = secure_filename(f.filename)
    if not nombre.lower().endswith(".xlsx"):
        return jsonify({"ok": False, "msg": "El archivo debe ser .xlsx"}), 400
    os.makedirs(ENTRADAS_DIR, exist_ok=True)
    ruta = os.path.join(ENTRADAS_DIR, nombre)
    f.save(ruta)
    licencias, error = _leer_seguro(ruta)
    if error:
        return jsonify({"ok": False, "msg": f"No pude leer el Excel: {error}"}), 400
    estado["excel"] = ruta
    return jsonify({"ok": True, "excel": ruta, "n": len(licencias), "licencias": licencias})


@app.route("/limpiar", methods=["POST"])
@login_required
def limpiar():
    """Olvida el Excel seleccionado y borra la tabla, resultados, log y progreso."""
    if estado["corriendo"]:
        return jsonify({"ok": False, "msg": "Hay un proceso en curso; espera a que termine."}), 409
    estado.update({"excel": "", "log": [], "resultados": [], "terminado": False,
                   "modo": None, "total": 0, "progreso": 0, "paso": ""})
    return jsonify({"ok": True})


@app.route("/procesar", methods=["POST"])
@login_required
def procesar():
    with _lock:
        if estado["corriendo"]:
            return jsonify({"ok": False, "msg": "Ya hay un proceso en curso."}), 409
        excel_path = estado["excel"]
        if not excel_path:
            return jsonify({"ok": False, "msg": "Primero elige un archivo Excel."}), 400
        if not os.path.exists(excel_path):
            return jsonify({"ok": False, "msg": f"No existe el Excel: {excel_path}"}), 400
        licencias, error = _leer_seguro(excel_path)
        if error:
            return jsonify({"ok": False, "msg": f"El Excel tiene problemas: {error}"}), 400
        if not licencias:
            return jsonify({"ok": False, "msg": "El Excel no tiene filas de licencias."}), 400
        enviar = request.json.get("enviar", False)
        fila = request.json.get("fila") or None
        headed = request.json.get("headed", False)
        estado.update({"corriendo": True, "modo": "envio" if enviar else "prueba",
                       "log": [], "resultados": [], "terminado": False,
                       "inicio": datetime.now().isoformat(),
                       "total": len(licencias), "progreso": 0,
                       "paso": "Preparando..."})
    threading.Thread(target=worker, args=(enviar, fila, headed, excel_path), daemon=True).start()
    return jsonify({"ok": True})


@app.route("/estado")
@login_required
def ver_estado():
    return jsonify(estado)


@app.route("/capturas/<path:nombre>")
@login_required
def captura(nombre):
    return send_from_directory(CAPTURAS_DIR, nombre)


@app.route("/documentos/<path:nombre>")
@login_required
def documento(nombre):
    return send_from_directory(DOCUMENTOS_DIR, nombre, as_attachment=True)


@app.route("/excel-procesado")
@login_required
def excel_procesado():
    p = estado.get("excel")
    if not p or not os.path.exists(p):
        return "No hay un Excel procesado disponible en esta sesión.", 404
    return send_file(os.path.abspath(p), as_attachment=True,
                     download_name=os.path.basename(p))


@app.route("/historial")
@login_required
def ver_historial():
    return jsonify(historial.leer())


@app.route("/historial/excel")
@login_required
def historial_excel():
    desde = request.args.get("desde") or None
    hasta = request.args.get("hasta") or None
    buf = historial.generar_excel_sueldos(desde, hasta)
    nombre = f"sueldos_{datetime.now():%Y%m%d_%H%M}.xlsx"
    return send_file(buf, as_attachment=True, download_name=nombre,
                     mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")


PLANTILLA_LOGIN = r"""
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ingreso · Licencias Médicas</title>
<style>
  :root { --morado:#8e2b8e; --morado2:#a13ba1; }
  * { box-sizing:border-box; }
  body { font-family:'Segoe UI',system-ui,sans-serif; margin:0; min-height:100vh;
         display:flex; align-items:center; justify-content:center;
         background:linear-gradient(135deg,#8e2b8e,#a13ba1); }
  .box { background:#fff; border-radius:14px; box-shadow:0 8px 30px rgba(0,0,0,.25);
         padding:34px 32px; width:340px; max-width:92vw; }
  .box h1 { font-size:19px; margin:0 0 4px; color:var(--morado); }
  .box p { margin:0 0 22px; font-size:13px; color:#888; }
  label { display:block; font-size:13px; color:#555; margin:12px 0 5px; }
  input { width:100%; padding:11px 12px; border:1px solid #ddd; border-radius:8px; font-size:14px; }
  input:focus { outline:none; border-color:var(--morado); }
  button { width:100%; margin-top:22px; border:0; border-radius:8px; padding:12px;
           background:var(--morado); color:#fff; font-size:15px; font-weight:600; cursor:pointer; }
  button:hover { background:var(--morado2); }
  .err { background:#fde5e5; color:#b02020; padding:9px 12px; border-radius:8px;
         font-size:13px; margin-top:16px; text-align:center; }
  .lock { font-size:34px; text-align:center; margin-bottom:6px; }
</style>
</head>
<body>
  <form class="box" method="post" action="/login">
    <div class="lock">🔒</div>
    <h1>Iniciar sesión</h1>
    <p>Ingreso Automático de Licencias Médicas</p>
    <label for="usuario">Usuario</label>
    <input type="text" id="usuario" name="usuario" autofocus required>
    <label for="clave">Clave</label>
    <input type="password" id="clave" name="clave" required>
    <button type="submit">Entrar</button>
    {% if error %}<div class="err">{{ error }}</div>{% endif %}
  </form>
</body>
</html>
"""

PLANTILLA = r"""
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ingreso Automático de Licencias Médicas</title>
<link rel="stylesheet" href="/static/flatpickr.min.css">
<style>
  :root { --morado:#8e2b8e; --morado2:#a13ba1; --verde:#2e8b57; --gris:#f4f4f7; }
  * { box-sizing:border-box; }
  body { font-family:'Segoe UI',system-ui,sans-serif; margin:0; background:var(--gris); color:#222; }
  header { background:linear-gradient(90deg,var(--morado),var(--morado2)); color:#fff; padding:18px 24px; }
  header h1 { margin:0; font-size:20px; }
  header p { margin:4px 0 0; opacity:.9; font-size:13px; }
  main { max-width:1100px; margin:22px auto; padding:0 16px; }
  .card { background:#fff; border-radius:10px; box-shadow:0 1px 4px rgba(0,0,0,.08); padding:18px 20px; margin-bottom:20px; }
  h2 { font-size:16px; margin:0 0 12px; color:var(--morado); }
  table { width:100%; border-collapse:collapse; font-size:13px; }
  th,td { text-align:left; padding:7px 9px; border-bottom:1px solid #eee; white-space:nowrap; }
  th { background:#faf5fb; color:#555; font-weight:600; }
  .scroll { overflow-x:auto; }
  .btns { display:flex; gap:12px; flex-wrap:wrap; align-items:center; margin-top:6px; }
  button { border:0; border-radius:7px; padding:11px 18px; font-size:14px; font-weight:600; cursor:pointer; color:#fff; }
  .b-prueba { background:var(--morado); }
  .b-envio  { background:var(--verde); }
  .b-sec { background:#666; }
  .dl-btn { background:var(--verde); color:#fff; text-decoration:none; border-radius:7px;
            padding:11px 18px; font-size:14px; font-weight:600; display:inline-block; }
  .dl-btn:hover { filter:brightness(1.08); }
  #menu { background:#fff; box-shadow:0 1px 4px rgba(0,0,0,.08); display:flex; gap:4px;
          padding:0 16px; position:sticky; top:0; z-index:10; }
  .menu-item { background:none; color:#666; border:0; border-bottom:3px solid transparent;
               padding:14px 20px; font-size:14px; font-weight:600; cursor:pointer; border-radius:0; }
  .menu-item:hover { color:var(--morado); }
  .menu-item.activo { color:var(--morado); border-bottom-color:var(--morado); }
  .filtro-fechas { display:flex; align-items:center; gap:14px; flex-wrap:wrap;
                   background:#faf5fb; border:1px solid #eadcee; border-radius:10px;
                   padding:12px 16px; margin-bottom:14px; }
  .filtro-tit { font-weight:600; color:var(--morado); font-size:13.5px; }
  .fp-campo { display:flex; flex-direction:column; gap:3px; }
  .fp-campo span { font-size:11.5px; color:#888; }
  .fp-campo input { border:1px solid #d8c8de; border-radius:8px; padding:9px 12px;
                    font-size:14px; width:140px; background:#fff; cursor:pointer; }
  .fp-campo input:focus { outline:none; border-color:var(--morado); }
  /* flatpickr en morado */
  .flatpickr-day.selected, .flatpickr-day.selected:hover { background:var(--morado); border-color:var(--morado); }
  .flatpickr-day.today { border-color:var(--morado); }
  .flatpickr-months .flatpickr-month, .flatpickr-weekdays, span.flatpickr-weekday { color:var(--morado); fill:var(--morado); }
  button:disabled { opacity:.5; cursor:not-allowed; }
  label.chk { font-size:13px; color:#555; display:flex; align-items:center; gap:6px; }
  #log { background:#1e1e1e; color:#d6d6d6; font-family:Consolas,monospace; font-size:12.5px;
         padding:12px; border-radius:8px; height:230px; overflow:auto; white-space:pre-wrap; }
  .estado-pill { display:inline-block; padding:3px 10px; border-radius:20px; font-size:12px; font-weight:600; }
  .ok { background:#e3f5e9; color:#1c7a3f; }
  .err{ background:#fde5e5; color:#b02020; }
  .res-item { border:1px solid #eee; border-radius:8px; padding:12px; margin-bottom:12px; }
  .res-item h3 { margin:0 0 6px; font-size:14px; }
  .res-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:4px 16px; font-size:13px; }
  .res-grid b { color:#666; font-weight:600; }
  .thumb { margin-top:8px; }
  .thumb img { max-width:100%; border:1px solid #ddd; border-radius:6px; }
  .aviso { background:#fff8e1; border:1px solid #ffe08a; padding:10px 14px; border-radius:8px; font-size:13px; color:#7a5b00; }
  .muted { color:#888; font-size:12.5px; }
  .fila-archivo { display:flex; gap:12px; flex-wrap:wrap; align-items:center; margin-bottom:12px; }
  .prog-head { display:flex; align-items:center; gap:12px; margin-bottom:4px; }
  .prog-head .pct { font-size:20px; font-weight:700; color:var(--morado); min-width:52px; }
  .prog-head .paso { font-size:13px; color:#555; }
  .spinner { width:22px; height:22px; border:3px solid #e7d8ee; border-top-color:var(--morado);
             border-radius:50%; animation:spin .8s linear infinite; display:none; flex:0 0 auto; }
  @keyframes spin { to { transform:rotate(360deg); } }
  .barra-out { background:#ececf2; border-radius:20px; height:20px; overflow:hidden; margin:8px 0 14px; }
  .barra-in { background:linear-gradient(90deg,var(--morado),var(--verde)); height:100%; width:0%;
              border-radius:20px; transition:width .45s ease;
              background-size:200% 100%; animation:desliza 2s linear infinite; }
  @keyframes desliza { to { background-position:200% 0; } }
</style>
</head>
<body>
<header style="display:flex;justify-content:space-between;align-items:center">
  <div>
    <h1>Ingreso Automático de Licencias Médicas</h1>
    <p>SIRH La Pintana (QA) · Personal Salud</p>
  </div>
  <a href="/logout" style="color:#fff;text-decoration:none;border:1px solid rgba(255,255,255,.6);padding:8px 14px;border-radius:7px;font-size:13px">Cerrar sesión</a>
</header>
<nav id="menu">
  <button class="menu-item activo" id="mi-procesar" onclick="mostrarVista('procesar')">📄 Procesar</button>
  <button class="menu-item" id="mi-historial" onclick="mostrarVista('historial')">🗂 Historial</button>
</nav>
<main>

  <div id="vista-procesar">
  <div class="card">
    <h2>Archivo Excel</h2>
    <div class="fila-archivo">
      <input type="file" id="archivo" accept=".xlsx">
      <button class="b-sec" onclick="cargarExcel()">📂 Cargar y previsualizar</button>
      <button class="b-sec" onclick="limpiarTabla()">🗑 Limpiar tabla</button>
    </div>
    <p class="muted">Excel actual: <b id="excelActual">{{ excel }}</b></p>
    {% if error_excel %}
      <div class="aviso" id="avisoExcel">⚠️ Problema leyendo el Excel: {{ error_excel }}</div>
    {% endif %}
    <div class="scroll">
      <table>
        <thead><tr>
          <th>Fila</th><th>RUT</th><th>Nombre</th><th>N° Licencia</th><th>Días</th>
          <th>Inicio</th><th>Tipo Licencia</th><th>Profesional</th><th>Especialidad</th>
          <th>Entidad</th><th>Reposo</th>
        </tr></thead>
        <tbody id="tablaLic">
        {% for l in licencias %}
          <tr>
            <td>{{ l._fila_excel }}</td><td>{{ l.rut }}</td><td>{{ l.nombre }}</td>
            <td>{{ l.num_licencia }}</td><td>{{ l.dias }}</td><td>{{ l.fecha_inicio }}</td>
            <td>{{ l.tipo_licencia }}</td><td>{{ l.nombre_profesional }}</td>
            <td>{{ l.especialidad }}</td><td>{{ l.entidad_salud }}</td><td>{{ l.tipo_reposo }}</td>
          </tr>
        {% endfor %}
        </tbody>
      </table>
    </div>
  </div>

  <div class="card">
    <h2>Procesar</h2>
    <div class="btns">
      <button class="b-prueba" id="btnPrueba" onclick="procesar(false)">▶ Procesar en PRUEBA (no guarda)</button>
      <button class="b-envio"  id="btnEnvio"  onclick="procesar(true)">💾 Procesar y GUARDAR (envío real)</button>
      <label class="chk"><input type="checkbox" id="headed"> Ver el navegador</label>
    </div>
    <p class="muted">Si no has elegido un Excel, al presionar Procesar se te pedirá el archivo.
       En modo <b>PRUEBA</b> se rellena todo y se toma una captura, pero <b>no se hace clic en Grabar</b>.</p>
  </div>

  <div class="card">
    <h2>Progreso <span id="pill"></span></h2>
    <div class="prog-head">
      <div class="spinner" id="spinner"></div>
      <span class="pct" id="pct">0%</span>
      <span class="paso" id="paso"></span>
    </div>
    <div class="barra-out"><div class="barra-in" id="barra"></div></div>
    <div id="log">(sin actividad)</div>
  </div>

  <div class="card" id="cardRes" style="display:none">
    <h2>Resultados</h2>
    <div class="btns" style="margin-bottom:10px">
      <a class="dl-btn" href="/excel-procesado">⬇ Descargar Excel procesado (con comentarios)</a>
    </div>
    <div id="resultados"></div>
  </div>

  </div><!-- fin vista-procesar -->

  <div id="vista-historial" style="display:none">
  <div class="card">
    <h2>Historial de licencias procesadas</h2>
    <div class="filtro-fechas">
      <span class="filtro-tit">📅 Filtrar por fecha</span>
      <div class="fp-campo"><span>Desde</span><input type="text" id="histDesde" placeholder="dd-mm-aaaa" readonly></div>
      <div class="fp-campo"><span>Hasta</span><input type="text" id="histHasta" placeholder="dd-mm-aaaa" readonly></div>
      <button class="b-sec" onclick="limpiarFiltroHist()">Limpiar filtro</button>
    </div>
    <div class="btns" style="margin-bottom:10px">
      <a class="dl-btn" id="btnSueldos" href="/historial/excel">⬇ Descargar Excel de sueldos</a>
      <button class="b-sec" onclick="cargarHistorial()">🔄 Actualizar</button>
    </div>
    <div class="scroll">
      <table>
        <thead><tr>
          <th>Fecha</th><th>RUT</th><th>Nombre</th><th>N° Licencia</th>
          <th>Excel origen</th><th>PDF Cobro SIL</th>
        </tr></thead>
        <tbody id="tablaHist"><tr><td colspan="6" class="muted">Sin licencias procesadas aún.</td></tr></tbody>
      </table>
    </div>
  </div>
  </div><!-- fin vista-historial -->

</main>

<script src="/static/flatpickr.min.js"></script>
<script src="/static/flatpickr.es.js"></script>
<script>
let timer = null;
let pendingEnviar = null;   // modo pendiente mientras se elige archivo

const inputArchivo = document.getElementById('archivo');

// Cuando el usuario elige un archivo en el diálogo
inputArchivo.addEventListener('change', ()=>{
  if(inputArchivo.files.length === 0) return;
  const modoPendiente = pendingEnviar;
  pendingEnviar = null;
  subirExcel().then(ok=>{
    if(ok && modoPendiente !== null) iniciar(modoPendiente);
  });
});

function subirExcel(){
  if(inputArchivo.files.length === 0) return Promise.resolve(false);
  const fd = new FormData();
  fd.append('archivo', inputArchivo.files[0]);
  return fetch('/subir', {method:'POST', body:fd}).then(r=>r.json()).then(d=>{
    if(!d.ok){ alert(d.msg || 'No se pudo cargar el Excel'); return false; }
    document.getElementById('excelActual').textContent = d.excel;
    const aviso = document.getElementById('avisoExcel'); if(aviso) aviso.remove();
    renderTabla(d.licencias);
    return true;
  });
}

function cargarExcel(){
  if(inputArchivo.files.length === 0){ inputArchivo.click(); return; }
  subirExcel();
}

function limpiarTabla(){
  fetch('/limpiar', {method:'POST'}).then(r=>r.json()).then(d=>{
    if(!d.ok){ alert(d.msg || 'No se pudo limpiar'); return; }
    // tabla y archivo
    document.getElementById('tablaLic').innerHTML = '';
    document.getElementById('excelActual').textContent = '(ninguno)';
    inputArchivo.value = '';
    const aviso = document.getElementById('avisoExcel'); if(aviso) aviso.remove();
    // resultados, log y progreso de la corrida anterior
    document.getElementById('resultados').innerHTML = '';
    document.getElementById('cardRes').style.display = 'none';
    document.getElementById('log').textContent = '(sin actividad)';
    document.getElementById('barra').style.width = '0%';
    document.getElementById('pct').textContent = '0%';
    document.getElementById('paso').textContent = '';
    document.getElementById('spinner').style.display = 'none';
    document.getElementById('pill').textContent = '';
  });
}

function renderTabla(licencias){
  const c = v => v==null ? '' : v;
  document.getElementById('tablaLic').innerHTML = licencias.map(l =>
    '<tr><td>'+c(l._fila_excel)+'</td><td>'+c(l.rut)+'</td><td>'+c(l.nombre)+'</td><td>'+
    c(l.num_licencia)+'</td><td>'+c(l.dias)+'</td><td>'+c(l.fecha_inicio)+'</td><td>'+
    c(l.tipo_licencia)+'</td><td>'+c(l.nombre_profesional)+'</td><td>'+c(l.especialidad)+
    '</td><td>'+c(l.entidad_salud)+'</td><td>'+c(l.tipo_reposo)+'</td></tr>'
  ).join('');
}

// Botones Procesar: si no hay archivo elegido, pide el archivo primero
function procesar(enviar){
  if(inputArchivo.files.length === 0){
    pendingEnviar = enviar;
    inputArchivo.click();      // abre el diálogo "de dónde está el Excel"
    return;
  }
  iniciar(enviar);
}

function iniciar(enviar){
  if(enviar && !confirm('Esto GUARDARÁ las licencias en el sistema (envío real). ¿Continuar?'))
    return;
  const headed = document.getElementById('headed').checked;
  fetch('/procesar', {method:'POST', headers:{'Content-Type':'application/json'},
      body: JSON.stringify({enviar, headed})})
    .then(r=>r.json()).then(d=>{
      if(!d.ok){ alert(d.msg || 'No se pudo iniciar'); return; }
      setBtns(true);
      // reinicia el indicador visual
      document.getElementById('spinner').style.display = 'block';
      document.getElementById('barra').style.width = '0%';
      document.getElementById('pct').textContent = '0%';
      document.getElementById('paso').textContent = 'Preparando...';
      if(timer) clearInterval(timer);
      timer = setInterval(actualizar, 900);
    });
}

function setBtns(dis){
  document.getElementById('btnPrueba').disabled = dis;
  document.getElementById('btnEnvio').disabled = dis;
}

function actualizar(){
  fetch('/estado').then(r=>{ if(r.status===401){ location.href='/login'; throw 0; } return r.json(); }).then(e=>{
    document.getElementById('log').textContent = (e.log||[]).join('\n') || '(sin actividad)';
    const log = document.getElementById('log'); log.scrollTop = log.scrollHeight;
    // barra de progreso
    const pct = e.progreso || 0;
    document.getElementById('barra').style.width = pct + '%';
    document.getElementById('pct').textContent = pct + '%';
    document.getElementById('paso').textContent = e.paso || '';
    document.getElementById('spinner').style.display = e.corriendo ? 'block' : 'none';
    const pill = document.getElementById('pill');
    if(e.corriendo){ pill.className='estado-pill ok'; pill.textContent='EN PROCESO ('+(e.modo||'')+')'; }
    else if(e.terminado){ pill.className='estado-pill ok'; pill.textContent='TERMINADO'; }
    else { pill.textContent=''; }
    pintarResultados(e.resultados||[]);
    if(!e.corriendo && e.terminado){ setBtns(false); clearInterval(timer); timer=null; cargarHistorial(); }
  });
}

function mostrarVista(v){
  document.getElementById('vista-procesar').style.display  = (v==='procesar') ? '' : 'none';
  document.getElementById('vista-historial').style.display = (v==='historial') ? '' : 'none';
  document.getElementById('mi-procesar').classList.toggle('activo', v==='procesar');
  document.getElementById('mi-historial').classList.toggle('activo', v==='historial');
  if(v==='historial') cargarHistorial();
}

let histData = [];

function cargarHistorial(){
  fetch('/historial').then(r=>{ if(r.status===401){ location.href='/login'; throw 0; } return r.json(); })
    .then(h=>{ histData = h || []; renderHistorial(); }).catch(()=>{});
}

function renderHistorial(){
  const desde = document.getElementById('histDesde').value;
  const hasta = document.getElementById('histHasta').value;
  const tb = document.getElementById('tablaHist');
  let datos = histData.filter(e=>{
    const f = (e.timestamp||'').slice(0,10);
    if(desde && f < desde) return false;
    if(hasta && f > hasta) return false;
    return true;
  });
  // actualizar el enlace de descarga con el filtro
  const q = []; if(desde) q.push('desde='+desde); if(hasta) q.push('hasta='+hasta);
  document.getElementById('btnSueldos').href = '/historial/excel' + (q.length?('?'+q.join('&')):'');
  if(!datos.length){ tb.innerHTML = '<tr><td colspan="6" class="muted">Sin licencias en el rango seleccionado.</td></tr>'; return; }
  tb.innerHTML = datos.slice().reverse().map(e=>
    '<tr><td>'+(e.timestamp||'')+'</td><td>'+(e.rut||'')+'</td><td>'+(e.nombre||'')+
    '</td><td>'+(e.num_licencia||'')+'</td><td>'+(e.excel_origen||'')+'</td><td>'+
    (e.pdf ? '<a href="/documentos/'+encodeURIComponent(e.pdf)+'">'+e.pdf+'</a>' : '—')+
    '</td></tr>'
  ).join('');
}

function limpiarFiltroHist(){
  try { fpDesde.clear(); fpHasta.clear(); } catch(e){}
  renderHistorial();
}

function campo(k,v){ return v ? '<div><b>'+k+':</b> '+v+'</div>' : ''; }

function tablaRemun(rm){
  const f = a => (a||[]).map(x=>'<td style="text-align:right">'+(x===''||x==null?'':Number(x).toLocaleString('es-CL'))+'</td>').join('');
  return '<div class="scroll" style="margin-top:8px"><table style="font-size:12px">'+
    '<tr><th>Remuneraciones</th>'+(rm.meses||[]).map(m=>'<th>'+m+'</th>').join('')+'</tr>'+
    '<tr><td>Imponible</td>'+f(rm.imponible)+'</tr>'+
    '<tr><td>Cotiz. AFP</td>'+f(rm.cotizacion_afp)+'</tr>'+
    '<tr><td>Salud</td>'+f(rm.salud)+'</tr>'+
    '<tr><td>Impuesto</td>'+f(rm.impuesto)+'</tr>'+
    '<tr><td>Líquido</td>'+f(rm.liquido)+'</tr>'+
    '</table></div>';
}

function pintarResultados(res){
  if(!res.length){ document.getElementById('cardRes').style.display='none'; return; }
  document.getElementById('cardRes').style.display='block';
  let html='';
  res.forEach(r=>{
    if(r.estado==='ERROR'){
      html += '<div class="res-item"><h3>Fila '+r.fila_excel+' · '+r.rut+
              ' <span class="estado-pill err">ERROR</span></h3><div class="muted">'+
              (r.error||'')+'</div></div>';
    } else {
      html += '<div class="res-item"><h3>Fila '+r.fila_excel+' · '+r.nombre_cargado+
              ' <span class="estado-pill ok">'+(r.modo||'OK')+'</span></h3>'+
              '<div class="res-grid">'+
                campo('N° Licencia', r.num_licencia)+
                campo('Reposo', r.fecha_inicio+' → '+r.fecha_fin+' ('+r.dias+' días, '+r.tipo_reposo+')')+
                campo('Tipo', r.tipo_licencia)+
                campo('Profesional', r.profesional)+
                campo('Apellidos', r.prof_apellidos)+
                campo('Especialidad', r.especialidad)+
                campo('Entidad', r.entidad_salud)+
                campo('Recuperación', (r.dias_calculados? r.dias_calculados+' día(s), valor '+r.valor_recuperacion : ''))+
                (r.pdf_cobro_sil ? '<div><b>Cobro SIL:</b> <a href="/documentos/'+
                   r.pdf_cobro_sil.split(/[\\/]/).pop()+'" target="_blank">descargar PDF</a>'+
                   (r.remuneraciones_escritas ? ' · remuneraciones escritas en el Excel ✓' : '')+'</div>' : '')+
                (r.error_cobro_sil ? '<div style="color:#b02020"><b>Cobro SIL:</b> '+r.error_cobro_sil+'</div>' : '')+
              '</div>'+
              (r.remuneraciones && r.remuneraciones.meses && r.remuneraciones.meses.length ? tablaRemun(r.remuneraciones) : '')+
              (r.captura ? '<div class="thumb"><a href="/'+r.captura.replace(/\\/g,'/')+
                 '" target="_blank"><img src="/'+r.captura.replace(/\\/g,'/')+'"></a></div>' : '')+
              '</div>';
    }
  });
  document.getElementById('resultados').innerHTML = html;
}

// Datepickers (flatpickr, en español) para el filtro del historial
const cfgFP = { locale: 'es', altInput: true, altFormat: 'd-m-Y', dateFormat: 'Y-m-d',
                allowInput: false, onChange: renderHistorial };
const fpDesde = flatpickr('#histDesde', cfgFP);
const fpHasta = flatpickr('#histHasta', cfgFP);

// Cargar el historial al abrir la página
cargarHistorial();
</script>
</body>
</html>
"""

if __name__ == "__main__":
    print(f"Código cargado (automation.py): {estado['version_codigo']}")
    print("Abre en tu navegador:  http://localhost:5000")
    app.run(host="127.0.0.1", port=5000, debug=False, threaded=True)
