#!/usr/bin/env python3
"""
Aplica los diffs reales de out_surya/*.adjudicated.jsonl al repo vedabase-original.
Misma lógica de localización por contexto que apply_scan2_corrections.py.

DRY_RUN = True  -> solo imprime, no escribe
DRY_RUN = False -> modifica los archivos
"""
import json, re, unicodedata
from pathlib import Path
from collections import defaultdict

REPO = Path('/Users/jaganat/git_projects/vedabase-original')
OUT  = Path('/Users/jaganat/git_projects/astro_vedabase/scripts/scan_audit/out_surya')

DRY_RUN = False  # cambiar a True para dry-run

# ── normalización ──────────────────────────────────────────────────────────────
def strip_diacritics(s):
    return ''.join(c for c in unicodedata.normalize('NFKD', s)
                   if not unicodedata.combining(c))

RAW_WORD_RE = re.compile(r'[A-Za-zÀ-ɏḀ-ỿĀ-ſ0-9]+', re.UNICODE)

def raw_tokens(raw):
    toks = []
    for m in RAW_WORD_RE.finditer(raw):
        norm = strip_diacritics(m.group()).lower()
        toks.append((norm, m.start(), m.end()))
    return toks

# ── localizar span en el archivo crudo ────────────────────────────────────────
def find_span_in_raw(raw, ctx):
    m = re.match(r'(.*)\[\[(.+?)\]\](.*)', ctx)
    if not m:
        return None
    left_norm  = m.group(1).strip().split()
    brac_words = m.group(2).strip().split()
    right_norm = m.group(3).strip().split()
    nb = len(brac_words)
    nl = min(5, len(left_norm))
    nr = min(4, len(right_norm))

    toks  = raw_tokens(raw)
    words = [t[0] for t in toks]

    for i in range(len(words) - nb + 1):
        if words[i:i+nb] != brac_words:
            continue
        if nl and i >= nl and words[i-nl:i] != left_norm[-nl:]:
            continue
        if nr and i+nb+nr <= len(words) and words[i+nb:i+nb+nr] != right_norm[:nr]:
            continue
        raw_start = toks[i][1]
        raw_end   = toks[i+nb-1][2]
        return (raw_start, raw_end, raw[raw_start:raw_end])
    return None

# ── calcular el reemplazo (recortar contexto solapado) ────────────────────────
def compute_replacement(orig_reading, right_ctx_words):
    orig_words = orig_reading.split()
    right_norm = [strip_diacritics(w).lower().strip('.,;:()') for w in right_ctx_words]
    trim = 0
    for k in range(1, len(orig_words) + 1):
        tail = [strip_diacritics(w).lower().strip('.,;:()') for w in orig_words[-k:]]
        if tail == right_norm[:k]:
            trim = k
    return ' '.join(orig_words[:len(orig_words) - trim]) if trim < len(orig_words) else orig_reading

def trim_left(replacement, left_ctx_words):
    rep_words = replacement.split()
    left_norm = [strip_diacritics(w).lower().strip('.,;:()') for w in left_ctx_words]
    trim = 0
    for k in range(1, len(rep_words) + 1):
        head = [strip_diacritics(w).lower().strip('.,;:()') for w in rep_words[:k]]
        if head == left_norm[-k:]:
            trim = k
    return ' '.join(rep_words[trim:]) if trim < len(rep_words) else replacement

# ── cargar todas las entradas reales ──────────────────────────────────────────
per_file = defaultdict(list)

for jf in sorted(OUT.glob('*.adjudicated.jsonl')):
    for line in jf.read_text().splitlines():
        if not line.strip():
            continue
        e = json.loads(line)
        if not e.get('llm', {}).get('is_real_diff'):
            continue
        per_file[e['file']].append(e)

# ── aplicar ───────────────────────────────────────────────────────────────────
stats = {'applied': 0, 'skipped_same': 0, 'not_found': 0}

for filerel, entries in sorted(per_file.items()):
    fp = REPO / filerel
    if not fp.exists():
        print(f"FILE NOT FOUND: {fp}")
        continue
    raw = fp.read_text(errors='ignore')
    modified = raw

    file_edits = []
    for e in entries:
        ctx = e['context']
        orig = e['llm']['original_reading']
        m = re.match(r'(.*)\[\[(.+?)\]\](.*)', ctx)
        if not m:
            continue
        left_norm  = m.group(1).strip().split()
        right_norm = m.group(3).strip().split()

        replacement = compute_replacement(orig, right_norm)
        replacement = trim_left(replacement, left_norm)

        result = find_span_in_raw(modified, ctx)
        if not result:
            print(f"  NOT FOUND: {filerel} | ctx={ctx[:70]}")
            stats['not_found'] += 1
            continue

        raw_start, raw_end, actual = result
        if strip_diacritics(actual).lower() == strip_diacritics(replacement).lower():
            stats['skipped_same'] += 1
            continue

        file_edits.append((raw_start, raw_end, actual, replacement))

    file_edits.sort(key=lambda x: x[0], reverse=True)
    for raw_start, raw_end, actual, replacement in file_edits:
        print(f"  {filerel}: [{repr(actual)}] → [{repr(replacement)}]")
        if not DRY_RUN:
            modified = modified[:raw_start] + replacement + modified[raw_end:]
        stats['applied'] += 1

    if not DRY_RUN and modified != raw:
        fp.write_text(modified, encoding='utf-8')

print()
print(f"=== {'DRY-RUN' if DRY_RUN else 'APPLIED'} ===")
print(f"  Applied:      {stats['applied']}")
print(f"  Skipped same: {stats['skipped_same']}")
print(f"  Not found:    {stats['not_found']}")
