
from docx import Document
from docx.shared import Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
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, dimensione piccola e padding.
    Aggiunge spaziatura appropriata sopra e sotto la tabella.
    """
    # Trova il paragrafo e inserisci la tabella subito dopo
    p_element = after_paragraph._element
    
    # Crea la tabella con 1 riga (header) e 5 colonne
    table = doc.add_table(rows=1, cols=5)
    table_element = table._element
    
    # Inserisci la tabella DOPO il paragrafo nel documento XML
    p_element.addnext(table_element)
    
    # Aggiungi un paragrafo vuoto DOPO la tabella per spaziatura
    empty_p = OxmlElement('w:p')
    table_element.addnext(empty_p)
    
    # Stile tabella: bordi visibili
    table.style = 'Table Grid'
    
    # Rimuovi eventuali spaziature eccessive nel paragrafo placeholder
    # Imposta spacing before e after a 0 per il paragrafo del placeholder
    pPr = after_paragraph._element.get_or_add_pPr()
    spacing = OxmlElement('w:spacing')
    spacing.set(qn('w:before'), '0')
    spacing.set(qn('w:after'), '120')  # 6pt dopo
    pPr.append(spacing)
    
    # Funzione helper per aggiungere padding alle celle
    def set_cell_padding(cell, top=100, bottom=100, left=100, right=100):
        """Imposta padding in twips (1/20 di punto). 100 twips = 5pt"""
        tc = cell._element
        tcPr = tc.get_or_add_tcPr()
        tcMar = OxmlElement('w:tcMar')
        
        for margin_name, value in [('top', top), ('bottom', bottom), ('left', left), ('right', right)]:
            node = OxmlElement(f'w:{margin_name}')
            node.set(qn('w:w'), str(value))
            node.set(qn('w:type'), 'dxa')
            tcMar.append(node)
        
        tcPr.append(tcMar)
    
    # Headers
    headers = ["CONTO CONTRATTUALE", "FATTURA", "SCADENZA", "IMPORTO", "INDIRIZZO UTENZA"]
    header_row = table.rows[0]
    for cell, title in zip(header_row.cells, headers):
        # Imposta padding
        set_cell_padding(cell, top=80, bottom=80, left=100, right=100)
        
        # Rimuovi paragrafi esistenti e crea nuovo con formattazione
        cell.text = ''
        p = cell.paragraphs[0]
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        run = p.add_run(title)
        run.font.name = 'Arial'
        run.font.size = Pt(9)
        run.font.bold = True
    
    # Righe dati
    for f in fatture:
        row = table.add_row()
        
        # Dati celle
        data_cells = [
            str(f.get("conto_contrattuale", "")),
            str(f.get("fattura", "")),
            str(f.get("scadenza", "")),
            "",  # Importo - gestito separatamente
            str(f.get("indirizzo_utenza", ""))
        ]
        
        # Formatta importo con simbolo €
        imp = f.get("importo", "")
        if isinstance(imp, (int, float)):
            imp_formatted = f"{imp:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
            data_cells[3] = f"€ {imp_formatted}"
        else:
            data_cells[3] = str(imp) if imp else ""
        
        # Applica dati e formattazione a tutte le celle
        for cell, text in zip(row.cells, data_cells):
            # Imposta padding
            set_cell_padding(cell, top=60, bottom=60, left=100, right=100)
            
            # Imposta testo con formattazione
            cell.text = ''
            p = cell.paragraphs[0]
            run = p.add_run(text)
            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])
