#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Loader Caja Fibromuebles — carga preview_v2_movimientos.csv a PocketBase (coleccion movimientos).

USO:
  python3 load_caja.py                 # DRY-RUN: valida y muestra que haria, NO escribe
  PB_ADMIN_EMAIL=... PB_ADMIN_PASSWORD=... python3 load_caja.py --commit   # escribe de verdad

Seguros:
  - dry-run por defecto
  - valida tipo/cuenta/categoria contra los enums de lib/contabilidad/domain.ts
  - antes de escribir, aborta si ya hay movimientos con fecha < 2026-04-01 (anti-duplicado)
  - cada registro lleva notas="odoo:<asiento>" para trazabilidad
"""
import csv, os, sys, json, urllib.request, urllib.parse, urllib.error
from collections import defaultdict

HERE = os.path.dirname(os.path.abspath(__file__))
SRC  = os.path.join(HERE, "preview_v2_movimientos.csv")
PB_URL = os.environ.get("PB_URL", "https://fibromuebles-db.bah-ia.com.ar").rstrip("/")
EMAIL  = os.environ.get("PB_ADMIN_EMAIL")
PASS   = os.environ.get("PB_ADMIN_PASSWORD")
COMMIT = "--commit" in sys.argv
FORCE  = "--force" in sys.argv

CUENTAS = {"Efectivo","Banco Macro","Cuenta DNI","Banco Nación","Cocos","Mercadopago"}
CAT_EGRESO = {"Materiales","Herrajes","Mano de obra","Sueldos / retiros","Servicios","Impuestos",
              "Alquiler / gastos del local","Herramientas / máquinas","Fletes / envíos",
              "Marketing / publicidad","Software / sistemas","Mantenimiento","Cuenta socio","Otros"}
CAT_INGRESO = {"Seña","Pago parcial","Saldo final","Venta directa","Ajuste / devolución","Otros"}

def req(method, url, token=None, body=None):
    data = json.dumps(body).encode() if body is not None else None
    r = urllib.request.Request(url, data=data, method=method)
    r.add_header("Content-Type", "application/json")
    if token: r.add_header("Authorization", token)
    def parse(code, raw):
        try: return code, json.loads(raw or "{}")
        except Exception: return code, {"_raw": raw[:300]}
    try:
        with urllib.request.urlopen(r) as resp:
            return parse(resp.status, resp.read().decode(errors="replace"))
    except urllib.error.HTTPError as e:
        return parse(e.code, e.read().decode(errors="replace"))
    except Exception as e:
        return 0, {"_err": str(e)}

# ---- leer CSV y armar payloads ----
rows = list(csv.DictReader(open(SRC, encoding="utf-8-sig")))
payloads, errors = [], []
for i, r in enumerate(rows, 2):
    tipo = r["tipo"]; cuenta = r["cuenta"]; cat = r["categoria"].strip()
    p = {
        "tipo": tipo,
        "monto": int(r["monto"]),
        "cuenta": cuenta,
        "detalle": r["detalle"],
        "fecha": r["fecha"],
        "check_odoo": True,
        "check_contabilidad": True,
        "notas": f"odoo:{r['numero_odoo']}" if r.get("numero_odoo") else "",
    }
    if tipo == "transferencia":
        p["cuenta_destino"] = r["cuenta_destino"]
    if cat:
        p["categoria"] = cat
    if r["numero_pedido"]:
        p["numero_pedido"] = r["numero_pedido"]
    # validaciones
    if tipo not in {"ingreso","egreso","transferencia"}: errors.append(f"fila {i}: tipo '{tipo}'")
    if cuenta not in CUENTAS: errors.append(f"fila {i}: cuenta '{cuenta}'")
    if p["monto"] <= 0: errors.append(f"fila {i}: monto {p['monto']}")
    if not (r["fecha"].startswith("2026-01") or r["fecha"].startswith("2026-02") or r["fecha"].startswith("2026-03")):
        errors.append(f"fila {i}: fecha fuera de ene-mar '{r['fecha']}'")
    if tipo == "transferencia":
        if r["cuenta_destino"] not in CUENTAS or r["cuenta_destino"] == cuenta:
            errors.append(f"fila {i}: cuenta_destino transfer '{r['cuenta_destino']}'")
    elif tipo == "egreso" and cat and cat not in CAT_EGRESO:
        errors.append(f"fila {i}: categoria egreso '{cat}'")
    elif tipo == "ingreso" and cat and cat not in CAT_INGRESO:
        errors.append(f"fila {i}: categoria ingreso '{cat}'")
    payloads.append(p)

# ---- resumen ----
def total(p): return sum(x["monto"] for x in payloads if p(x))
print(f"FUENTE: {os.path.basename(SRC)}  ({len(payloads)} registros)")
print(f"PB_URL: {PB_URL}")
print(f"MODO  : {'COMMIT (escribe)' if COMMIT else 'DRY-RUN (no escribe)'}")
print()
by = defaultdict(int)
for p in payloads: by[p["tipo"]] += 1
for t in ("ingreso","egreso","transferencia"):
    print(f"  {t:<14} {by[t]:>4}   ${total(lambda x:x['tipo']==t):>13,}")
print(f"  con numero_pedido: {sum(1 for p in payloads if p.get('numero_pedido'))}")
print(f"  con notas odoo   : {sum(1 for p in payloads if p['notas'])}")
print(f"  check_odoo / check_contabilidad: TRUE en todos")
print()
if errors:
    print(f"!!! {len(errors)} ERRORES DE VALIDACION (no se carga nada):")
    for e in errors[:30]: print("   ", e)
    sys.exit(1)
print("Validacion OK. Ejemplos de payload:")
for p in payloads[:3]:
    print("   ", json.dumps(p, ensure_ascii=False))
print("   ...")
for p in [x for x in payloads if x["tipo"]=="transferencia"][:1]:
    print("    (transfer)", json.dumps(p, ensure_ascii=False))

if not COMMIT:
    print("\nDRY-RUN terminado. Para escribir: setear PB_ADMIN_EMAIL/PASSWORD y correr con --commit")
    sys.exit(0)

# ---- COMMIT ----
if not EMAIL or not PASS:
    print("\nFATAL: faltan PB_ADMIN_EMAIL / PB_ADMIN_PASSWORD en el entorno."); sys.exit(1)
print("\nAutenticando superuser...")
code, data = req("POST", f"{PB_URL}/api/collections/_superusers/auth-with-password",
                 body={"identity": EMAIL, "password": PASS})
if code != 200:
    print(f"FATAL: auth fallo {code}: {data}"); sys.exit(1)
token = data["token"]
# anti-duplicado
q = urllib.parse.urlencode({"filter": 'fecha < "2026-04-01"', "perPage": 1})
code, data = req("GET", f"{PB_URL}/api/collections/movimientos/records?{q}", token=token)
existing = data.get("totalItems", 0)
print(f"Movimientos ene-mar ya existentes en PB: {existing}")
if existing > 0 and not FORCE:
    print("ABORTO: ya hay movimientos ene-mar (evito duplicar). Usá --force si estás seguro."); sys.exit(1)
ok = fail = 0
for n, p in enumerate(payloads, 1):
    code, data = req("POST", f"{PB_URL}/api/collections/movimientos/records", token=token, body=p)
    if code == 200: ok += 1
    else:
        fail += 1
        if fail <= 10: print(f"  FALLO fila {n}: {code} {data}")
    if n % 50 == 0: print(f"  ... {n}/{len(payloads)}")
print(f"\nLISTO: creados {ok}, fallos {fail}")
q = urllib.parse.urlencode({"filter": 'fecha < "2026-04-01"', "perPage": 1})
code, data = req("GET", f"{PB_URL}/api/collections/movimientos/records?{q}", token=token)
print(f"Verificacion: ahora hay {data.get('totalItems',0)} movimientos ene-mar en PB")
