#!/usr/bin/env python3
"""
Backfill de Ingresos/Retiros (historico_ir) desde el snapshot de historico_empleados
para las razones sociales que la API /rrhh/ingresos-retiros NO devuelve.

Contexto
--------
CLUB (FC Bogota) + FEB (Belvedere) comparten el NIT 900345450 / bucket
"Faranda_Bogota". El endpoint /rrhh/ingresos-retiros dejo de incluir ese bucket
a partir de jun-2026 (ene-may 2026 si venia), aunque los datos SI existen en Zeus
y en /rrhh/empleados-activos-detalle. Resultado: los KPI de "Ingresos" y "Retiros"
del dashboard salen en 0 para Belvedere/FC Bogota y no cuadran con "Activos Fin".

Este script reconstruye esos movimientos comparando el snapshot mensual de
historico_empleados (mes vs. mes anterior) e inserta las filas faltantes en
historico_ir con origen='snapshot'. La logica es la misma que usa cierre_mes.py
de aqui en adelante (completar_ir_desde_snapshot); este script solo cubre los
meses ya cerrados antes del fix.

Uso
---
    python3 backfill_ir_snapshot.py --dry-run                 # solo mostrar
    python3 backfill_ir_snapshot.py                           # aplicar (jun-2026 -> ultimo periodo)
    python3 backfill_ir_snapshot.py --desde 2026-06 --hasta 2026-08
    python3 backfill_ir_snapshot.py --razones CLUB,FEB
    python3 backfill_ir_snapshot.py --force                   # rehace las filas origen='snapshot' del rango

Correr en la maquina que hace los cierres (Mac mini, ~/rrhh-report, .venv/bin/python).
Idempotente: si una razon ya tiene filas IR del periodo, no la toca.
"""
from __future__ import annotations

import argparse
import logging
import sys

import psycopg2

from app_config import db_config
from cierre_mes import (
    IR_FALLBACK_SNAPSHOT_RAZONES,
    completar_ir_desde_snapshot,
)


def periodos(desde: str, hasta: str):
    ya, ma = int(desde[:4]), int(desde[5:7])
    yh, mh = int(hasta[:4]), int(hasta[5:7])
    y, m = ya, ma
    while (y, m) <= (yh, mh):
        yield y, m
        y, m = (y + 1, 1) if m == 12 else (y, m + 1)


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__,
                                 formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--desde", default="2026-06", help="periodo inicial YYYY-MM (default 2026-06)")
    ap.add_argument("--hasta", default=None, help="periodo final YYYY-MM (default: MAX(fecha) de historico_empleados)")
    ap.add_argument("--razones", default=None,
                    help="lista separada por comas (default: %s)" % ",".join(sorted(IR_FALLBACK_SNAPSHOT_RAZONES)))
    ap.add_argument("--force", action="store_true",
                    help="borra primero las filas origen='snapshot' del rango y las vuelve a generar")
    ap.add_argument("--dry-run", action="store_true", help="no escribe, solo muestra el conteo")
    args = ap.parse_args()

    logging.basicConfig(level=logging.INFO, format="%(message)s")
    log = logging.getLogger("backfill_ir_snapshot")

    razones = (set(r.strip().upper() for r in args.razones.split(",")) if args.razones
               else set(IR_FALLBACK_SNAPSHOT_RAZONES))

    cfg = db_config()
    conn = psycopg2.connect(cfg["dsn"]) if "dsn" in cfg else psycopg2.connect(**cfg)
    conn.autocommit = False

    with conn.cursor() as cur:
        cur.execute("ALTER TABLE historico_ir ADD COLUMN IF NOT EXISTS origen VARCHAR(16) DEFAULT 'api'")
        conn.commit()
        hasta = args.hasta
        if not hasta:
            cur.execute("SELECT TO_CHAR(MAX(fecha), 'YYYY-MM') FROM historico_empleados")
            hasta = cur.fetchone()[0]

    rango = list(periodos(args.desde, hasta))
    log.info(f"Razones: {sorted(razones)}")
    log.info(f"Rango:   {args.desde} .. {hasta}  ({len(rango)} meses)")
    log.info(f"Modo:    {'DRY-RUN' if args.dry_run else 'APLICAR'}{' + FORCE' if args.force else ''}")
    log.info("")

    if args.force and not args.dry_run:
        with conn.cursor() as cur:
            cur.execute(
                "DELETE FROM historico_ir WHERE origen='snapshot' AND razon_social = ANY(%s) "
                "AND periodo BETWEEN %s AND %s",
                (sorted(razones), args.desde, hasta),
            )
            log.info(f"[--force] borradas {cur.rowcount} filas origen='snapshot' previas")
        conn.commit()

    total = 0
    for ano, mes in rango:
        if args.dry_run:
            # Simular: contar entradas/salidas sin insertar
            periodo = f"{ano}-{mes:02d}"
            pa, pm = (ano - 1, 12) if mes == 1 else (ano, mes - 1)
            with conn.cursor() as cur:
                for rs in sorted(razones):
                    cur.execute("SELECT COUNT(*) FROM historico_ir WHERE periodo=%s AND razon_social=%s",
                                (periodo, rs))
                    if cur.fetchone()[0] > 0:
                        log.info(f"  {periodo} {rs}: ya tiene filas IR, se omite")
                        continue
                    cur.execute("SELECT codigo_empleado FROM historico_empleados WHERE fecha=%s AND razon_social=%s",
                                (f"{ano}-{mes:02d}-01", rs))
                    cur_c = {r[0] for r in cur.fetchall()}
                    cur.execute("SELECT codigo_empleado FROM historico_empleados WHERE fecha=%s AND razon_social=%s",
                                (f"{pa}-{pm:02d}-01", rs))
                    prev_c = {r[0] for r in cur.fetchall()}
                    if not prev_c:
                        log.info(f"  {periodo} {rs}: sin snapshot previo, no se puede derivar")
                        continue
                    i, r = len(cur_c - prev_c), len(prev_c - cur_c)
                    total += i + r
                    log.info(f"  {periodo} {rs}: sintetizaria {i} ingresos + {r} retiros")
        else:
            total += completar_ir_desde_snapshot(conn, ano, mes, razones, log)

    log.info("")
    log.info(f"{'[DRY-RUN] ' if args.dry_run else ''}Total filas sintetizadas: {total}")
    conn.close()
    return 0


if __name__ == "__main__":
    sys.exit(main())
