#!/usr/bin/env python3
"""
Regenera `clasificacion_articulos.csv` recorriendo las cartas semanales y
extrayendo los links que cada una agrupa por encabezado (Artículos, Evangelio,
Eucaristía, Multimedia, EFFA). Paridad con `wp-content/mu-plugins/fea-carta-portada.php`.

Output: post_id, post_title, categoria_propuesta, seccion_original, carta_id, carta_titulo, carta_fecha

ALCANCE (vs. CSV histórico de marzo 2026):
- Cubre las 5 secciones estándar: comentario, articulo, eucaristia, multimedia, effa.
- Sub-clasifica por posición dentro de "Evangelio y comentarios al Evangelio":
    · pos 1 → `lectura` (cita del evangelio)
    · pos 2 → `comentario_editorial`
    · pos 3+ → `comentario`
  (regla del editor, confirmada contra el CSV histórico)
- NO cubre todavía:
    · `lectura` dentro de eucaristía (lecturas bíblicas — el viejo las separa, aquí van todas a `eucaristia`)
    · `otro` (catch-all del catch-all)
    · `noticia` (subgrupo poco usado, 12 filas en el viejo)
    · Encabezados de fiestas especiales ("Domingo de Resurrección", "Navidad", "Vigilia Pascual", etc.)

Para regenerar el CSV con cobertura completa habría que ampliar el mapping en
SECTION_PATTERNS y SECTION_LABELS con reglas adicionales. El CSV histórico
existente (raíz del repo) sirve como baseline para esa cobertura granular.

Uso:
  python3 regenerar_clasificacion_csv.py [--out /path/clasificacion_articulos.csv] [--diff /path/csv_marzo.csv]

Issue: rafa/feadulta#42
"""
import argparse, csv, os, re, subprocess, sys

try:
    import pymysql
except ImportError:
    sys.exit('pymysql requerido: pip install --user pymysql')

# Mapping sección encabezado → cat slug (debe espejar fea-carta-portada.php)
SECTION_PATTERNS = [
    ('comentario',  re.compile(r'Evangelio\s+y\s+comentarios\s+al\s+Evangelio', re.I)),
    ('articulo',    re.compile(r'Art[ií]culos\s+seleccionados\s+para\s+la\s+semana', re.I)),
    ('eucaristia',  re.compile(r'Para\s+unas\s+eucarist[ií]as\s+m[áa]s\s+participativas', re.I)),
    ('multimedia',  re.compile(r'Material\s+multimedia', re.I)),
    ('effa',        re.compile(r'Escuela\s+EFFA', re.I)),
]

# Nombres “bonitos” usados en seccion_original (verbatim del CSV histórico)
SECTION_LABELS = {
    'comentario':  'Evangelio y comentarios al Evangelio',
    'articulo':    'Artículos seleccionados para la semana',
    'eucaristia':  'Para unas eucaristías más participativas y actuales',
    'multimedia':  'Material multimedia',
    'effa':        'Escuela EFFA',
}

CAT_PROPUESTA = {
    'comentario':  'comentario',
    'articulo':    'articulo',
    'eucaristia':  'eucaristia',
    'multimedia':  'multimedia',
    'effa':        'effa',
}

# Sub-clasificación posicional dentro de la sección "Evangelio y comentarios al Evangelio".
# El editor SIEMPRE coloca: 1º lectura del evangelio, 2º comentario editorial, 3º+ comentarios.
SUBCAT_EVANGELIO_BY_POS = ['lectura', 'comentario_editorial']  # resto = 'comentario'

HREF_RX = re.compile(r'href=["\']([^"\']+)["\']', re.I)
WP_SLUG_RX  = re.compile(r'(?:^|/)fea/([a-z0-9\-]+)/?(?:[?#]|$)', re.I)
K2_ITEM_RX  = re.compile(r'/item/(\d+)-[^/"]+\.html', re.I)
RESERVED_SLUGS = {'wp-admin','wp-content','category','tag','author','page','en','fr','it','pt'}


def get_conn():
    ip = subprocess.run(
        ['docker', 'inspect', 'wordpress-mysql', '--format',
         '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'],
        capture_output=True, text=True, check=True,
    ).stdout.strip()
    return pymysql.connect(
        host=ip, user='wordpress_user', password='wordpress_pass',
        database='wordpress_db', charset='utf8mb4', autocommit=True,
    )


def mysql(query, conn=None):
    """Ejecuta query y devuelve filas (lista de tuplas) — pymysql, sin parsing CLI."""
    own = False
    if conn is None:
        conn = get_conn(); own = True
    try:
        with conn.cursor() as c:
            c.execute(query)
            return list(c.fetchall())
    finally:
        if own: conn.close()


def fetch_cartas(conn):
    """Todas las cartas (cat 6 actual, 22 semana pasada, 21 otras) + sus contenidos."""
    q = """
    SELECT p.ID, p.post_title, DATE(p.post_date), p.post_content
    FROM wp_posts p
    WHERE p.post_status='publish' AND p.post_type='post'
      AND p.ID IN (
        SELECT DISTINCT tr.object_id FROM wp_term_relationships tr
        JOIN wp_term_taxonomy tt ON tt.term_taxonomy_id=tr.term_taxonomy_id
        WHERE tt.term_id IN (6, 21, 22) AND tt.taxonomy='category'
      )
    ORDER BY p.post_date DESC;
    """
    return mysql(q, conn)


def build_lookups(conn):
    """Construye dicts slug→post_id y k2_id→post_id para no machacar la BD por cada link.
    Para slugs duplicados (varios posts con mismo slug), se usa el MÁS RECIENTE
    (criterio espejo del mu-plugin fea-carta-portada.php tras el bug detectado en #38).
    """
    print('Cargando lookups (slug y k2_id) ...', file=sys.stderr, flush=True)
    slug_to_id = {}
    rows = mysql("""
        SELECT p1.post_name, p1.ID
        FROM wp_posts p1
        WHERE p1.post_status='publish' AND p1.post_type='post' AND p1.post_name<>''
        ORDER BY p1.post_date DESC;
    """, conn)
    for r in rows:
        slug = r[0]
        if slug not in slug_to_id:  # primero (más reciente) gana
            slug_to_id[slug] = int(r[1])

    k2_to_id = {}
    rows = mysql("""
        SELECT meta_value, MAX(post_id) FROM wp_postmeta
        WHERE meta_key='_fgj2wp_old_k2_id' AND meta_value<>''
        GROUP BY meta_value;
    """, conn)
    for r in rows:
        try:
            k2_to_id[int(r[0])] = int(r[1])
        except (ValueError, TypeError):
            continue

    print(f'  slugs: {len(slug_to_id)}  k2_ids: {len(k2_to_id)}', file=sys.stderr)
    return slug_to_id, k2_to_id


def fetch_titles(ids, conn):
    if not ids: return {}
    ids_str = ','.join(str(i) for i in ids)
    rows = mysql(f"SELECT ID, post_title FROM wp_posts WHERE ID IN ({ids_str});", conn)
    return {int(r[0]): r[1] for r in rows}


def url_to_post_id(url, slug_to_id, k2_to_id):
    m = WP_SLUG_RX.search(url)
    if m:
        slug = m.group(1).lower()
        if slug not in RESERVED_SLUGS and slug in slug_to_id:
            return slug_to_id[slug]
    m = K2_ITEM_RX.search(url)
    if m:
        k2 = int(m.group(1))
        if k2 in k2_to_id:
            return k2_to_id[k2]
    return None


def extract_sections(html_content):
    """Devuelve dict {section_slug: [post_id, ...]} basándose en encabezados.
    NOTA: los post_ids aún no están resueltos aquí — devuelve hrefs en su lugar.
    """
    positions = []
    for slug, rx in SECTION_PATTERNS:
        m = rx.search(html_content)
        if m:
            positions.append((m.start(), slug))
    if not positions:
        return {}
    positions.sort()
    positions.append((len(html_content), None))

    out = {}
    for i in range(len(positions) - 1):
        start, slug = positions[i]
        end = positions[i+1][0]
        segment = html_content[start:end]
        hrefs = HREF_RX.findall(segment)
        # Dedup preservando orden
        seen, urls = set(), []
        for h in hrefs:
            if h not in seen:
                seen.add(h); urls.append(h)
        out[slug] = urls
    return out


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--out', default='/tmp/clasificacion_articulos_regen.csv')
    ap.add_argument('--diff', help='CSV de referencia para mostrar diff')
    args = ap.parse_args()

    conn = get_conn()
    slug_to_id, k2_to_id = build_lookups(conn)

    print('Leyendo cartas ...', file=sys.stderr, flush=True)
    cartas = fetch_cartas(conn)
    print(f'  cartas: {len(cartas)}', file=sys.stderr)

    rows_out = []
    needed_titles = set()
    n_unresolved = 0
    n_resolved = 0

    for c in cartas:
        carta_id, carta_title, carta_fecha, content = c
        carta_id = int(carta_id)
        if not content:
            continue

        sections = extract_sections(content)
        for slug, urls in sections.items():
            label = SECTION_LABELS[slug]
            default_cat = CAT_PROPUESTA[slug]
            # Filtrar a posts resueltos manteniendo orden
            resolved = []
            for url in urls:
                pid = url_to_post_id(url, slug_to_id, k2_to_id)
                if pid is None:
                    n_unresolved += 1
                    continue
                resolved.append(pid)
            for pos, pid in enumerate(resolved):
                # Sub-clasificación posicional para evangelio
                if slug == 'comentario' and pos < len(SUBCAT_EVANGELIO_BY_POS):
                    cat = SUBCAT_EVANGELIO_BY_POS[pos]
                else:
                    cat = default_cat
                n_resolved += 1
                needed_titles.add(pid)
                rows_out.append({
                    'post_id': pid,
                    'categoria_propuesta': cat,
                    'seccion_original': label,
                    'carta_id': carta_id,
                    'carta_titulo': carta_title,
                    'carta_fecha': carta_fecha,
                })

    print(f'Resueltos: {n_resolved}  Sin resolver: {n_unresolved}', file=sys.stderr)
    print('Cargando títulos ...', file=sys.stderr, flush=True)
    titles = fetch_titles(list(needed_titles), conn)
    conn.close()

    # Escribir CSV
    cols = ['post_id', 'post_title', 'categoria_propuesta', 'seccion_original', 'carta_id', 'carta_titulo', 'carta_fecha']
    with open(args.out, 'w', newline='', encoding='utf-8') as f:
        w = csv.DictWriter(f, fieldnames=cols, quoting=csv.QUOTE_MINIMAL)
        w.writeheader()
        for r in rows_out:
            r['post_title'] = titles.get(r['post_id'], '')
            w.writerow(r)
    print(f'Escrito: {args.out} ({len(rows_out)} filas)')

    # Diff opcional
    if args.diff and os.path.exists(args.diff):
        from collections import defaultdict, Counter
        def load(path):
            d = defaultdict(set)  # (post_id, cat) → set((carta_id, seccion))
            cats_by_post = defaultdict(set)
            with open(path, encoding='utf-8-sig') as fh:
                r = csv.DictReader(fh)
                for row in r:
                    pid = row.get('post_id','')
                    cat = row.get('categoria_propuesta','')
                    if not pid: continue
                    cats_by_post[pid].add(cat)
            return cats_by_post
        old = load(args.diff)
        new = load(args.out)
        old_keys = set(old.keys())
        new_keys = set(new.keys())
        print('\n=== DIFF ===')
        print(f'posts en CSV viejo:     {len(old_keys)}')
        print(f'posts en CSV nuevo:     {len(new_keys)}')
        print(f'solo en viejo:          {len(old_keys - new_keys)}')
        print(f'solo en nuevo:          {len(new_keys - old_keys)}')
        common = old_keys & new_keys
        same_cats = sum(1 for k in common if old[k] == new[k])
        diff_cats = len(common) - same_cats
        print(f'común con mismas cats:  {same_cats}')
        print(f'común con cats distintas: {diff_cats}')


if __name__ == '__main__':
    main()
