#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Limpieza de nombres de pacientes (reversible).

Pasos:
  1) Respaldo CSV id -> nombre original.
  2) Normaliza: control chars, frases marcador (no usar exp...), barras, espacios, signos.
  3) Title Case con particulas en minuscula (de, del, de la, los, las, y, etc.).
  4) Dry-run por defecto; con --apply actualiza solo las filas que cambian.
"""
import csv
import os
import re
import sys
import unicodedata
from datetime import datetime

import psycopg2

DSN = "host=localhost dbname=skeen_odoo user=skeen password=skeen_dev_2026"

PARTICULAS = {
    'de', 'del', 'la', 'las', 'los', 'y', 'e', 'da', 'do', 'das', 'dos',
    'di', 'van', 'von', 'der', 'den', 'san', 'santa', 'mc',
}

# Frases marcador inyectadas por el personal dentro del nombre
MARKER_RE = re.compile(
    r'\bno\s*usar(\s+(este\s+)?exp(?:ediente)?)?\b[!.,;:/\\-]*',
    re.IGNORECASE,
)
ERROR_RE = re.compile(r'\berror\b[!.,;:/\\-]*', re.IGNORECASE)
SLASHES_RE = re.compile(r'[/\\]+')
MULTISPACE_RE = re.compile(r'\s{2,}')
LEAD_TRAIL_JUNK_RE = re.compile(r'^[\s.\-_,;:]+|[\s.\-_,;:]+$')
CONTROL_RE = re.compile(r'[\x00-\x1f\x7f]')


def cap_token(tok, first):
    low = tok.lower()
    if not first and low in PARTICULAS:
        return low
    # Mantener siglas cortas (iniciales) en mayuscula: J, GPE -> J, Gpe
    if len(tok) <= 1:
        return tok.upper()
    return low.capitalize()


def clean_name(raw):
    if raw is None:
        return raw
    s = CONTROL_RE.sub('', raw)
    s = SLASHES_RE.sub(' ', s)          # barras -> espacio
    s = MARKER_RE.sub(' ', s)           # quita "no usar exp..."
    s = ERROR_RE.sub(' ', s)            # quita "ERROR" suelto
    s = s.replace('.', ' ')             # puntos (iniciales, GPE.) -> espacio
    s = MULTISPACE_RE.sub(' ', s)
    s = LEAD_TRAIL_JUNK_RE.sub('', s).strip()
    s = MULTISPACE_RE.sub(' ', s)
    if not s:
        return s
    toks = s.split(' ')
    toks = [cap_token(t, i == 0) for i, t in enumerate(toks) if t]
    return ' '.join(toks)


def main():
    apply = '--apply' in sys.argv
    conn = psycopg2.connect(DSN)
    conn.autocommit = False
    cur = conn.cursor()
    cur.execute("SELECT id, name FROM res_partner WHERE is_patient ORDER BY id")
    rows = cur.fetchall()

    changes = []
    for rid, name in rows:
        new = clean_name(name)
        if new != name:
            changes.append((rid, name, new))

    ts = datetime.now().strftime('%Y%m%d_%H%M%S')
    backup_path = f'/root/migracion/datos/name_backup_{ts}.csv'
    with open(backup_path, 'w', newline='', encoding='utf-8') as f:
        w = csv.writer(f)
        w.writerow(['id', 'name_original'])
        for rid, name, _ in changes:
            w.writerow([rid, name])

    print(f'Total pacientes: {len(rows)}')
    print(f'A modificar:     {len(changes)}')
    print(f'Sin cambios:     {len(rows) - len(changes)}')
    print(f'Respaldo:        {backup_path}')
    print()
    print('--- Ejemplos antes -> despues (30) ---')
    shown = 0
    # prioriza los que tenian marcador o signos
    def score(c):
        n = c[1] or ''
        return (('no usar' in n.lower()) * 10) + (('/' in n) * 5) + (('  ' in n) * 2)
    for rid, old, new in sorted(changes, key=score, reverse=True)[:30]:
        print(f'  [{rid}] "{old}"  ->  "{new}"')
        shown += 1
    print(f'  ({shown} ejemplos)')

    if not apply:
        print('\nDRY-RUN: no se aplicaron cambios. Ejecuta con --apply para aplicar.')
        conn.rollback()
        cur.close(); conn.close()
        return

    updated = 0
    for rid, old, new in changes:
        cur.execute("UPDATE res_partner SET name = %s WHERE id = %s", (new, rid))
        updated += 1
    conn.commit()
    print(f'\nAPLICADO: {updated} nombres actualizados.')
    cur.close(); conn.close()


if __name__ == '__main__':
    main()
