"""Procesa un PDF masivo de liquidaciones (1 funcionario por página): extrae
RUT, mes, año, días trabajados, total imponible y el número de página de cada uno.
No divide en archivos: el PDF individual se genera del masivo al descargar.

Uso (segundo plano, escribe avance en estado/<job>.json):
    python liquidaciones_masivo.py --pdf "ruta.pdf" --job <id>
Uso directo (JSON a stdout):
    python liquidaciones_masivo.py --pdf "ruta.pdf"
"""
import argparse
import json
import os
import re
import time
import pdfplumber

ESTADO_DIR = "estado"
MESES = {"ENERO": 1, "FEBRERO": 2, "MARZO": 3, "ABRIL": 4, "MAYO": 5, "JUNIO": 6,
         "JULIO": 7, "AGOSTO": 8, "SEPTIEMBRE": 9, "OCTUBRE": 10, "NOVIEMBRE": 11, "DICIEMBRE": 12}


def _rut_canon(rut):
    s = str(rut or "").upper().replace(".", "").replace(" ", "")
    if "-" in s:
        cuerpo, _, dv = s.partition("-")
    else:
        cuerpo, dv = s[:-1], s[-1:]
    cuerpo = cuerpo.lstrip("0") or "0"
    return f"{cuerpo}-{dv}"


def _extraer(t):
    tu = t.upper()
    rut = re.search(r"RUT ?: ?([0-9]{6,9}-?[0-9Kk])", t)
    dias = re.search(r"(\d{1,3})\s+\d{2}/\d{2}/\d{4}", t)
    imp = re.search(r"([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+)\s*\n\s*Total Haberes", t)
    per = re.search(r"REMUNERACIONES DE ([A-ZÑ]+) DE (\d{4})", tu)
    # Entidad pagadora (previsión salud): "SALUD : FONASA" (evita PLAN SALUD / FDO. SALUD,
    # cuyo valor es numérico y no matchea el grupo de letras).
    salud = re.search(r"\bSALUD ?: ?([A-ZÑ][A-ZÑ .]+?)(?:\s+PLAN\b|\r|\n|$)", tu)
    entidad = salud.group(1).strip() if salud else None
    # Tipo de contrato (columna Calidad Jurídica): INDEFINIDO / PLAZO FIJO.
    # La celda suele venir en singular o plural ("PLAZO FIJO" / "PLAZOS FIJOS") y a
    # veces partida en dos líneas ("PLAZOS" / "FIJOS"), así que se busca cada palabra
    # por separado en vez de la frase completa.
    # Un contrato de REMPLAZO siempre tiene fecha de término real (no es indefinido)
    # y el portal solo ofrece Sí/No para indefinido -> se trata como PLAZO FIJO.
    if "PLAZO" in tu and "FIJO" in tu:
        contrato = "PLAZO FIJO"
    elif "INDEFINID" in tu:
        contrato = "INDEFINIDO"
    elif "REMPLAZO" in tu or "REEMPLAZO" in tu:
        contrato = "PLAZO FIJO"
    else:
        contrato = None
    # AFP (Institución Previsional): "AFP : CAPITAL 11,44%" -> "CAPITAL".
    # Caso especial: la AFP "Uno" se imprime como "AFP : AFP UNO 10,46%" (su
    # nombre comercial repite la palabra "AFP"), a diferencia de las demás
    # ("CAPITAL", "HABITAT", ...) que no llevan ese prefijo -> se quita, porque
    # el select del portal LME solo tiene el nombre corto ("UNO", no "AFP UNO").
    afp_m = re.search(r"\bAFP ?: ?([A-ZÑ][A-ZÑ .]+?)\s+[\d,]+ ?%", tu)
    afp = afp_m.group(1).strip() if afp_m else None
    if afp:
        afp = re.sub(r"^A\.?\s?F\.?\s?P\.?\s+", "", afp).strip()
    # "NO COTIZA A.F.P." / "SIN PREVISION AFP": no es el nombre de una AFP
    # concreta, pero el portal LME SÍ tiene "NO COTIZA A.F.P." como opción
    # seleccionable de Institución Previsional (bajo Régimen D.L. 3500 A.F.P.)
    # -> se normaliza a ese texto exacto en vez de descartarlo (decisión
    # explícita del usuario), así _completar_previsional() lo selecciona igual
    # que cualquier otra AFP.
    if afp and ("NO COTIZA" in afp or "SIN PREVISION" in afp):
        afp = "NO COTIZA A.F.P."
    # Fecha de ingreso: "FECHA INGRESO : 10/04/1995". En el portal LME, el caso
    # verificado (empleada con datos pre-cargados) mostró "Fecha Primera Afiliación"
    # y "Fecha Contrato" IGUALES a esta fecha de ingreso de la liquidación.
    fing_m = re.search(r"FECHA INGRESO ?: ?(\d{2}/\d{2}/\d{4})", tu)
    fecha_ingreso = fing_m.group(1) if fing_m else None
    # Algunos registros migrados de sistemas antiguos traen "01/01/1900" como
    # fecha "vacía" (sentinela) en vez de un dato real; usarla tal cual mandaría
    # una fecha de afiliación/contrato falsa al portal LME, así que se descarta.
    if fecha_ingreso and int(fecha_ingreso[-4:]) < 1920:
        fecha_ingreso = None
    # Fallback: si no hay una FECHA INGRESO confiable, usar la Fecha Inicio del
    # contrato vigente (columna "Fecha Inicio" de la tabla: "<días trab.> <fecha
    # inicio> <fecha término>") como mejor aproximación disponible -> decisión
    # explícita del usuario, general (no importa si el contrato es indefinido
    # -con "31/12/2999" de término- o a plazo fijo con término real; folio
    # 130556825-9/RUT 17488992-9 y RUT 7779748-3 vinieron con FECHA INGRESO
    # "01/01/1900" pero la tabla sí traía una Fecha Inicio real).
    if not fecha_ingreso:
        fi_m = re.search(r"\d{1,3}\s+(\d{2}/\d{2}/\d{4})\s+\d{2}/\d{2}/\d{4}", tu)
        if fi_m:
            fecha_ingreso = fi_m.group(1)
    return {
        "rut": _rut_canon(rut.group(1)) if rut else None,
        "dias": int(dias.group(1)) if dias else None,
        "imponible": int(imp.group(2).replace(".", "")) if imp else None,
        "mes": MESES.get(per.group(1)) if per else None,
        "anio": int(per.group(2)) if per else None,
        "entidad": entidad,
        "contrato": contrato,
        "afp": afp,
        "fecha_ingreso": fecha_ingreso,
    }


def procesar(pdf_path, progreso=None):
    """Devuelve (registros, mes, anio). Cada registro: rut, dias, imponible, mes, anio, pagina."""
    registros = []
    mes = anio = None
    with pdfplumber.open(pdf_path) as pl:
        total = len(pl.pages)
        for i, page in enumerate(pl.pages):
            d = _extraer(page.extract_text() or "")
            if d["rut"] and d["mes"] and d["anio"]:
                d["pagina"] = i + 1
                registros.append(d)
                mes, anio = d["mes"], d["anio"]
            if progreso and (i % 25 == 0 or i == total - 1):
                progreso(i + 1, total)
    return registros, mes, anio


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--pdf", required=True)
    ap.add_argument("--job", default=None)
    args = ap.parse_args()

    if not args.job:
        regs, mes, anio = procesar(args.pdf)
        print(json.dumps({"ok": True, "n": len(regs), "mes": mes, "anio": anio,
                          "registros": regs}, ensure_ascii=True))
        return

    os.makedirs(ESTADO_DIR, exist_ok=True)
    ruta = os.path.join(ESTADO_DIR, f"{args.job}.json")
    estado = {"corriendo": True, "terminado": False, "progreso": 0,
              "total": 0, "procesados": 0, "mes": None, "anio": None,
              "n": 0, "registros": [], "error": None}

    def guardar():
        tmp = ruta + ".tmp"
        try:
            with open(tmp, "w", encoding="utf-8") as f:
                json.dump(estado, f, ensure_ascii=True)
        except Exception:
            return
        for _ in range(20):
            try:
                os.replace(tmp, ruta); return
            except PermissionError:
                time.sleep(0.1)
            except Exception:
                break
        try:
            with open(ruta, "w", encoding="utf-8") as f:
                json.dump(estado, f, ensure_ascii=True)
        except Exception:
            pass

    def prog(hechas, total):
        estado["total"] = total
        estado["procesados"] = hechas
        estado["progreso"] = int(hechas / total * 100) if total else 0
        guardar()

    guardar()
    try:
        regs, mes, anio = procesar(args.pdf, progreso=prog)
        estado.update({"registros": regs, "n": len(regs), "mes": mes, "anio": anio, "progreso": 100})
    except Exception as e:
        estado["error"] = str(e)
    finally:
        estado["corriendo"] = False
        estado["terminado"] = True
        guardar()


if __name__ == "__main__":
    main()
