"""
Simulate importing missing payments for Expro Transit Inc (account 3673).
DRY RUN — reads legacy data, shows what would be created, and produces
a visual timeline of invoice vs payment balance.

Run inside erpnext-backend-1:
  /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/simulate_payment_import.py
"""
import frappe
import pymysql
import os
import json
from datetime import datetime

os.chdir("/home/frappe/frappe-bench/sites")
frappe.init(site="erp.gigafibre.ca", sites_path=".")
frappe.connect()
print("Connected:", frappe.local.site)

conn = pymysql.connect(
    host="legacy-db",
    user="facturation",
    password="*******",
    database="gestionclient",
    cursorclass=pymysql.cursors.DictCursor
)

ACCOUNT_ID = 3673
CUSTOMER = "CUST-cbf03814b9"
CUSTOMER_NAME = "Expro Transit Inc."

# ═══════════════════════════════════════════════════════════════
# STEP 1: Load all legacy payments + allocations
# ═══════════════════════════════════════════════════════════════
with conn.cursor() as cur:
    cur.execute("""
        SELECT p.id, p.date_orig, p.amount, p.applied_amt, p.type, p.reference
        FROM payment p
        WHERE p.account_id = %s
        ORDER BY p.date_orig ASC
    """, (ACCOUNT_ID,))
    legacy_payments = cur.fetchall()

    cur.execute("""
        SELECT pi.payment_id, pi.invoice_id, pi.amount
        FROM payment_item pi
        JOIN payment p ON p.id = pi.payment_id
        WHERE p.account_id = %s
    """, (ACCOUNT_ID,))
    legacy_allocs = cur.fetchall()

conn.close()

# Build allocation map: payment_id -> [{invoice_id, amount}]
alloc_map = {}
for a in legacy_allocs:
    pid = a["payment_id"]
    if pid not in alloc_map:
        alloc_map[pid] = []
    alloc_map[pid].append({
        "invoice_id": a["invoice_id"],
        "amount": float(a["amount"] or 0)
    })

# Which PEs already exist in ERPNext?
erp_pes = frappe.db.sql("""
    SELECT name FROM "tabPayment Entry" WHERE party = %s AND docstatus = 1
""", (CUSTOMER,), as_dict=True)
erp_pe_ids = set()
for pe in erp_pes:
    try:
        erp_pe_ids.add(int(pe["name"].split("-")[1]))
    except:
        pass

# ═══════════════════════════════════════════════════════════════
# STEP 2: Load all ERPNext invoices
# ═══════════════════════════════════════════════════════════════
erp_invoices = frappe.db.sql("""
    SELECT name, posting_date, grand_total, outstanding_amount, status
    FROM "tabSales Invoice"
    WHERE customer = %s AND docstatus = 1
    ORDER BY posting_date ASC
""", (CUSTOMER,), as_dict=True)

inv_map = {}
for inv in erp_invoices:
    inv_map[inv["name"]] = {
        "date": str(inv["posting_date"]),
        "total": float(inv["grand_total"]),
        "outstanding": float(inv["outstanding_amount"]),
        "status": inv["status"],
    }

# ═══════════════════════════════════════════════════════════════
# STEP 3: Determine which payments to create
# ═══════════════════════════════════════════════════════════════
to_create = []
for p in legacy_payments:
    if p["id"] in erp_pe_ids:
        continue  # Already exists

    dt = datetime.fromtimestamp(p["date_orig"]).strftime("%Y-%m-%d") if p["date_orig"] else None
    amount = float(p["amount"] or 0)
    ptype = (p["type"] or "").strip()
    ref = (p["reference"] or "").strip()

    # Map legacy type to ERPNext mode
    mode_map = {
        "paiement direct": "Virement",
        "cheque": "Chèque",
        "carte credit": "Carte de crédit",
        "credit": "Note de crédit",
        "reversement": "Note de crédit",
    }
    mode = mode_map.get(ptype, ptype)

    # Get allocations
    allocations = alloc_map.get(p["id"], [])
    refs = []
    for a in allocations:
        sinv_name = "SINV-{}".format(a["invoice_id"])
        refs.append({
            "reference_doctype": "Sales Invoice",
            "reference_name": sinv_name,
            "allocated_amount": a["amount"],
        })

    to_create.append({
        "pe_name": "PE-{}".format(p["id"]),
        "legacy_id": p["id"],
        "date": dt,
        "amount": amount,
        "type": ptype,
        "mode": mode,
        "reference": ref,
        "allocations": refs,
    })

print("\n" + "=" * 70)
print("PAYMENT IMPORT SIMULATION — {} ({})".format(CUSTOMER_NAME, CUSTOMER))
print("=" * 70)
print("Legacy payments:     {}".format(len(legacy_payments)))
print("Already in ERPNext:  {}".format(len(erp_pe_ids)))
print("TO CREATE:           {}".format(len(to_create)))
total_to_create = sum(p["amount"] for p in to_create)
print("Total to import:     ${:,.2f}".format(total_to_create))

# ═══════════════════════════════════════════════════════════════
# STEP 4: Build timeline showing running balance
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 70)
print("VISUAL BALANCE TIMELINE (with new payments)")
print("=" * 70)

# Combine all events: invoices and payments (existing + to-create)
events = []

# Add invoices
for inv in erp_invoices:
    events.append({
        "date": str(inv["posting_date"]),
        "type": "INV",
        "name": inv["name"],
        "amount": float(inv["grand_total"]),
        "detail": inv["status"],
    })

# Add existing ERPNext payments
existing_pes = frappe.db.sql("""
    SELECT name, posting_date, paid_amount
    FROM "tabPayment Entry"
    WHERE party = %s AND docstatus = 1
""", (CUSTOMER,), as_dict=True)
for pe in existing_pes:
    events.append({
        "date": str(pe["posting_date"]),
        "type": "PAY",
        "name": pe["name"],
        "amount": float(pe["paid_amount"]),
        "detail": "existing",
    })

# Add new (simulated) payments
for p in to_create:
    events.append({
        "date": p["date"] or "2012-01-01",
        "type": "PAY",
        "name": p["pe_name"],
        "amount": p["amount"],
        "detail": "NEW " + p["mode"],
    })

# Sort by date, then INV before PAY on same date
events.sort(key=lambda e: (e["date"], 0 if e["type"] == "INV" else 1))

# Print timeline with running balance
balance = 0.0
total_invoiced = 0.0
total_paid = 0.0

# Group by year for readability
current_year = None
print("\n{:<12} {:<5} {:<16} {:>12} {:>12}   {}".format(
    "DATE", "TYPE", "DOCUMENT", "AMOUNT", "BALANCE", "DETAIL"))
print("-" * 85)

for e in events:
    year = e["date"][:4]
    if year != current_year:
        if current_year:
            print("  {:>55} {:>12}".format("--- year-end ---", "${:,.2f}".format(balance)))
        current_year = year
        print("\n  ── {} ──".format(year))

    if e["type"] == "INV":
        balance += e["amount"]
        total_invoiced += e["amount"]
        sign = "+"
    else:
        balance -= e["amount"]
        total_paid += e["amount"]
        sign = "-"

    marker = " ◀ NEW" if "NEW" in e.get("detail", "") else ""
    print("{:<12} {:<5} {:<16} {:>12} {:>12}   {}{}".format(
        e["date"],
        e["type"],
        e["name"],
        "{}${:,.2f}".format(sign, abs(e["amount"])),
        "${:,.2f}".format(balance),
        e["detail"],
        marker,
    ))

print("-" * 85)
print("\nFINAL SUMMARY:")
print("  Total invoiced:  ${:,.2f}".format(total_invoiced))
print("  Total paid:      ${:,.2f}".format(total_paid))
print("  Final balance:   ${:,.2f}".format(balance))
print("  Should be:       $0.00" if abs(balance) < 0.02 else "  ⚠ MISMATCH!")

# ═══════════════════════════════════════════════════════════════
# STEP 5: Show sample of what the PE documents would look like
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 70)
print("SAMPLE PAYMENT ENTRY DOCUMENTS (first 5)")
print("=" * 70)
for p in to_create[:5]:
    print("\n  PE Name:    {}".format(p["pe_name"]))
    print("  Date:       {}".format(p["date"]))
    print("  Amount:     ${:,.2f}".format(p["amount"]))
    print("  Mode:       {}".format(p["mode"]))
    print("  Reference:  {}".format(p["reference"] or "—"))
    print("  Allocations:")
    for a in p["allocations"]:
        print("    → {} = ${:,.2f}".format(a["reference_name"], a["allocated_amount"]))

# ═══════════════════════════════════════════════════════════════
# STEP 6: Identify potential issues
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 70)
print("POTENTIAL ISSUES")
print("=" * 70)

# Check for payments referencing invoices that don't exist in ERPNext
missing_invs = set()
for p in to_create:
    for a in p["allocations"]:
        if a["reference_name"] not in inv_map:
            missing_invs.add(a["reference_name"])

if missing_invs:
    print("⚠ {} payments reference invoices NOT in ERPNext:".format(len(missing_invs)))
    for m in sorted(missing_invs)[:10]:
        print("    {}".format(m))
else:
    print("✓ All payment allocations reference existing invoices")

# Check for credits (negative-type payments)
credits = [p for p in to_create if p["type"] in ("credit", "reversement")]
if credits:
    print("\n⚠ {} credit/reversement entries (not regular payments):".format(len(credits)))
    for c in credits:
        print("    {} date={} amount=${:,.2f} type={}".format(c["pe_name"], c["date"], c["amount"], c["type"]))
else:
    print("✓ No credit entries to handle specially")

# Payments without allocations
no_alloc = [p for p in to_create if not p["allocations"]]
if no_alloc:
    print("\n⚠ {} payments without invoice allocations:".format(len(no_alloc)))
    for p in no_alloc:
        print("    {} date={} amount=${:,.2f}".format(p["pe_name"], p["date"], p["amount"]))
else:
    print("✓ All payments have invoice allocations")

print("\n" + "=" * 70)
print("DRY RUN COMPLETE — no changes made")
print("=" * 70)
