
from docx import Document
from docx.shared import Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
from pathlib import Path
import json
import sys

PLACEHOLDERS = {
    # New template tokens -> supported JSON keys (fallbacks included)
    "{{indirizzo_destinatario}}": ["indirizzo_destinatario","indirizzo destinatario","destinatario_indirizzo"],
    "{{data}}": ["data","data_lettera"],
    "{{BP}}": ["BP","bp","codice_bp","codice BP","codiceBp"],
    "{{intestatario_contratto}}": ["intestatario_contratto","intestatario","dati_intestatario.denominazione"],
    "{{residuo}}": ["residuo","importo_totale_dovuto","totale_dovuto"],
    "{{cm_telefono}}": ["credit_manager.telefono","cm.telefono","telefono_cm"],
    "{{cm_email}}": ["credit_manager.email","cm.email","email_cm"],
    "{{cm_whatsapp}}": ["credit_manager.whatsapp","cm.whatsapp","whatsapp_cm"],
    # Backward compatibility with previous template I generated
    "{{codice_bp}}": ["BP","bp","codice_bp","codice BP","codiceBp"],
    "{{intestatario}}": ["intestatario","intestatario_contratto","dati_intestatario.denominazione"],
    "{{TABELLA_FATTURE}}": []  # handled separately
}

def get_by_path(d, path):
    """Get nested key via dot notation, return '' if missing."""
    cur = d
    for part in path.split("."):
        if isinstance(cur, dict) and part in cur:
            cur = cur[part]
        else:
            return ""
    return cur if cur is not None else ""

def value_for_token(data, token):
    keys = PLACEHOLDERS[token]
    for k in keys:
        if "." in k:
            val = get_by_path(data, k)
        else:
            val = data.get(k, "")
        if val not in (None, ""):
            return str(val)
    return ""

def replace_in_paragraph(paragraph, mapping):
    """
    Sostituisce i placeholder in un paragrafo.
    Gestisce il caso in cui Word divida i placeholder tra più runs.
    """
    # Controlla se c'è qualcosa da sostituire nel testo completo
    full_text = paragraph.text
    needs_replacement = False
    for token in mapping.keys():
        if token in full_text:
            needs_replacement = True
            break
    
    if not needs_replacement:
        return  # Nessun placeholder trovato, skip
    
    # Se ci sono placeholder, ricostruisci il testo completo sostituendo
    new_text = full_text
    for token, value in mapping.items():
        if token in new_text:
            new_text = new_text.replace(token, value)
    
    # Se il testo è cambiato, aggiorna il paragrafo
    if new_text != full_text:
        # Cancella tutti i runs esistenti
        for run in paragraph.runs:
            run.text = ""
        # Crea un nuovo run con il testo sostituito
        if paragraph.runs:
            paragraph.runs[0].text = new_text
        else:
            paragraph.add_run(new_text)

def replace_in_table(table, mapping):
    for row in table.rows:
        for cell in row.cells:
            for p in cell.paragraphs:
                replace_in_paragraph(p, mapping)

def find_paragraph_with(doc, token):
    for p in doc.paragraphs:
        if token in p.text:
            return p
    return None

def insert_fatture_table(doc, after_paragraph, fatture):
    """
    Inserisce la tabella fatture DOPO il paragrafo specificato.
    Applica formattazione con font Arial e dimensione piccola.
    """
    # Trova l'indice del paragrafo di riferimento
    paragraph_index = None
    for i, p in enumerate(doc.paragraphs):
        if p == after_paragraph:
            paragraph_index = i
            break
    
    if paragraph_index is None:
        # Fallback: aggiungi alla fine se non trova il paragrafo
        table = doc.add_table(rows=1, cols=5)
    else:
        # Inserisci la tabella dopo il paragrafo specifico
        # Aggiungi un paragrafo vuoto dopo il placeholder per posizionare la tabella
        p = doc.paragraphs[paragraph_index]._element
        table_element = doc.add_table(rows=1, cols=5)._element
        p.addnext(table_element)
        table = table_element
        # Ricarica il documento per ottenere l'oggetto tabella corretto
        for t in doc.tables:
            if t._element == table_element:
                table = t
                break
    
    # Stile tabella: bordi visibili
    table.style = 'Table Grid'
    
    # Headers
    headers = ["CONTO CONTRATTUALE", "FATTURA", "SCADENZA", "IMPORTO", "INDIRIZZO UTENZA"]
    header_row = table.rows[0]
    for i, (cell, title) in enumerate(zip(header_row.cells, headers)):
        cell.text = title
        # Formatta header: grassetto, Arial 9pt
        for paragraph in cell.paragraphs:
            for run in paragraph.runs:
                run.font.name = 'Arial'
                run.font.size = Pt(9)
                run.font.bold = True
            paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    # Righe dati
    for f in fatture:
        row = table.add_row().cells
        row[0].text = str(f.get("conto_contrattuale",""))
        row[1].text = str(f.get("fattura",""))
        row[2].text = str(f.get("scadenza",""))
        
        # Formatta importo
        imp = f.get("importo","")
        if isinstance(imp, (int,float)):
            imp = f"{imp:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
        row[3].text = str(imp)
        
        row[4].text = str(f.get("indirizzo_utenza",""))
        
        # Applica font Arial 8pt a tutte le celle della riga
        for cell in row:
            for paragraph in cell.paragraphs:
                for run in paragraph.runs:
                    run.font.name = 'Arial'
                    run.font.size = Pt(8)
    
    return table

def generate(json_path, template_path, out_path):
    print(f"[DEBUG] Caricamento JSON: {json_path}")
    data = json.loads(Path(json_path).read_text(encoding="utf-8"))
    
    print(f"[DEBUG] Dati JSON caricati:")
    print(f"  - intestatario_contratto: '{data.get('intestatario_contratto', 'MANCANTE')}'")
    print(f"  - BP: '{data.get('BP', 'MANCANTE')}'")
    print(f"  - data: '{data.get('data', 'MANCANTE')}'")
    print(f"  - residuo: '{data.get('residuo', 'MANCANTE')}'")
    
    print(f"[DEBUG] Caricamento template: {template_path}")
    doc = Document(template_path)

    # Build mapping for all tokens present in the document
    mapping = {}
    # Scan paragraphs and tables to know which tokens appear
    tokens_present = set()
    for p in doc.paragraphs:
        for token in PLACEHOLDERS.keys():
            if token in p.text:
                tokens_present.add(token)
    for t in doc.tables:
        for row in t.rows:
            for cell in row.cells:
                for p in cell.paragraphs:
                    for token in PLACEHOLDERS.keys():
                        if token in p.text:
                            tokens_present.add(token)

    print(f"[DEBUG] Token trovati nel template: {tokens_present}")

    for token in tokens_present:
        if token in ("{{TABELLA_FATTURE}}","{{tabella_fatture}}"):
            continue
        mapping[token] = value_for_token(data, token)
    
    print(f"[DEBUG] Mapping creato:")
    for token, value in mapping.items():
        status = "OK" if value else "VUOTO"
        print(f"  [{status}] {token} = '{value}'")

    # Replace tokens in paragraphs
    print(f"[DEBUG] Inizio sostituzione nei paragrafi...")
    replaced_count = 0
    for i, p in enumerate(doc.paragraphs):
        old_text = p.text
        replace_in_paragraph(p, mapping)
        if p.text != old_text:
            replaced_count += 1
            print(f"  [SOSTITUITO] Paragrafo {i}: '{old_text[:50]}...' -> '{p.text[:50]}...'")
    print(f"[DEBUG] Paragrafi modificati: {replaced_count}/{len(doc.paragraphs)}")
    
    # Replace in tables as well
    print(f"[DEBUG] Inizio sostituzione nelle tabelle...")
    table_replaced = 0
    for t in doc.tables:
        replace_in_table(t, mapping)
        table_replaced += 1
    print(f"[DEBUG] Tabelle processate: {table_replaced}")

    # Handle fatture table token (supports both {{tabella_fatture}} and {{TABELLA_FATTURE}})
    fatture = data.get("fatture", [])
    print(f"[DEBUG] Gestione tabella fatture: {len(fatture)} righe")
    for token in ("{{tabella_fatture}}","{{TABELLA_FATTURE}}"):
        p = find_paragraph_with(doc, token)
        if p is not None:
            print(f"[DEBUG] Trovato token tabella: {token}")
            p.text = p.text.replace(token, "")
            insert_fatture_table(doc, p, fatture)
            break  # insert only once

    print(f"[DEBUG] Salvataggio documento: {out_path}")
    
    # Se BP è valorizzato, usa BP come nome file
    bp_value = data.get('BP', '').strip()
    if bp_value:
        # Sostituisci il nome file con BP.docx
        out_dir = Path(out_path).parent
        new_filename = f"{bp_value}.docx"
        out_path = str(out_dir / new_filename)
        print(f"[DEBUG] BP valorizzato, rinomino file in: {new_filename}")
    
    Path(out_path).parent.mkdir(parents=True, exist_ok=True)
    doc.save(out_path)
    print(f"[DEBUG] ✓ Documento salvato con successo: {out_path}")

if __name__ == "__main__":
    if len(sys.argv) != 4:
        print("Usage: python generate_letter.py <dati.json> <template.docx> <output.docx>")
        sys.exit(1)
    generate(sys.argv[1], sys.argv[2], sys.argv[3])
