#!/usr/bin/env python3
"""
Prepend C- to all customer names.

Current state: customers have raw legacy_customer_id as name
  e.g. LPB4, 114796350603272, DOMIL5149490230

After: C-LPB4, C-114796350603272, C-DOMIL5149490230

New customers: C-10000000034941+ (naming_series C-.##############)

Two-phase rename to avoid PK collisions:
  Phase A: old → _TMP_C-old
  Phase B: _TMP_C-old → C-old
"""
import os, sys, time

os.chdir("/home/frappe/frappe-bench/sites")
import frappe
frappe.init(site="erp.gigafibre.ca", sites_path=".")
frappe.connect()
frappe.local.flags.ignore_permissions = True
print(f"Connected: {frappe.local.site}")

DRY_RUN = "--dry-run" in sys.argv
if DRY_RUN:
    print("*** DRY RUN ***")


# ═══════════════════════════════════════════════════════════════
# STEP 1: Build mapping — prepend C- to every customer name
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("STEP 1: BUILD MAPPING")
print("=" * 60)

customers = frappe.db.sql("""
    SELECT name FROM "tabCustomer" ORDER BY name
""", as_dict=True)

mapping = {}  # old → new
new_used = set()
skipped = 0

for c in customers:
    old = c["name"]
    # Skip if already has C- prefix (idempotent)
    if old.startswith("C-"):
        skipped += 1
        continue
    new = f"C-{old}"
    if new in new_used:
        new = f"{new}-dup{len(new_used)}"
    new_used.add(new)
    mapping[old] = new

print(f"Total customers: {len(customers)}")
print(f"Will rename: {len(mapping)}")
print(f"Already C- prefixed (skip): {skipped}")

# Samples
for old, new in list(mapping.items())[:15]:
    print(f"  {old:40s} → {new}")

if DRY_RUN:
    print("\n*** DRY RUN complete ***")
    sys.exit(0)

if not mapping:
    print("Nothing to rename!")
    sys.exit(0)


# ═══════════════════════════════════════════════════════════════
# STEP 2: Create temp mapping table
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("STEP 2: TEMP MAPPING TABLE")
print("=" * 60)

frappe.db.sql('DROP TABLE IF EXISTS _cust_cpre_map')
frappe.db.sql("""
    CREATE TEMP TABLE _cust_cpre_map (
        old_name VARCHAR(140) PRIMARY KEY,
        new_name VARCHAR(140) NOT NULL
    )
""")
items = list(mapping.items())
for start in range(0, len(items), 1000):
    batch = items[start:start + 1000]
    frappe.db.sql(
        "INSERT INTO _cust_cpre_map (old_name, new_name) VALUES " +
        ",".join(["(%s, %s)"] * len(batch)),
        [v for pair in batch for v in pair]
    )
frappe.db.commit()
print(f"  {len(mapping)} mappings loaded")


# ═══════════════════════════════════════════════════════════════
# STEP 3: Update all FK references
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("STEP 3: UPDATE FK REFERENCES")
print("=" * 60)

fk_tables = [
    ("tabSales Invoice", "customer", ""),
    ("tabPayment Entry", "party", ""),
    ("tabGL Entry", "party", ""),
    ("tabPayment Ledger Entry", "party", ""),
    ("tabIssue", "customer", ""),
    ("tabService Location", "customer", ""),
    ("tabService Subscription", "customer", ""),
    ("tabSubscription", "party", ""),
    ("tabService Equipment", "customer", ""),
    ("tabDispatch Job", "customer", ""),
    ("tabDynamic Link", "link_name", "AND t.link_doctype = 'Customer'"),
    ("tabComment", "reference_name", "AND t.reference_doctype = 'Customer'"),
    ("tabCommunication", "reference_name", "AND t.reference_doctype = 'Customer'"),
    ("tabVersion", "docname", "AND t.ref_doctype = 'Customer'"),
]

for table, col, extra in fk_tables:
    t0 = time.time()
    try:
        frappe.db.sql(f"""
            UPDATE "{table}" t SET "{col}" = m.new_name
            FROM _cust_cpre_map m
            WHERE t."{col}" = m.old_name {extra}
        """)
        frappe.db.commit()
        print(f"  {table:35s} {col:20s} [{time.time()-t0:.1f}s]")
    except Exception as e:
        frappe.db.rollback()
        err = str(e)[:80]
        if "does not exist" not in err:
            print(f"  {table:35s} ERR: {err}")


# ═══════════════════════════════════════════════════════════════
# STEP 4: Two-phase rename customers
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("STEP 4: RENAME CUSTOMERS (two-phase)")
print("=" * 60)

# Phase A: old → _TMP_C-old
t0 = time.time()
for start in range(0, len(items), 500):
    batch = items[start:start + 500]
    cases = " ".join(f"WHEN '{old}' THEN '_TMP_{new}'" for old, new in batch)
    old_names = "','".join(old for old, _ in batch)
    frappe.db.sql(f"""
        UPDATE "tabCustomer"
        SET name = CASE name {cases} END
        WHERE name IN ('{old_names}')
    """)
    if (start + 500) % 5000 < 500:
        frappe.db.commit()
        print(f"  A: {min(start+500, len(items))}/{len(items)}")
frappe.db.commit()
print(f"  Phase A done [{time.time()-t0:.1f}s]")

# Phase B: _TMP_C-old → C-old
t0 = time.time()
for start in range(0, len(items), 500):
    batch = items[start:start + 500]
    cases = " ".join(f"WHEN '_TMP_{new}' THEN '{new}'" for _, new in batch)
    temp_names = "','".join(f"_TMP_{new}" for _, new in batch)
    frappe.db.sql(f"""
        UPDATE "tabCustomer"
        SET name = CASE name {cases} END
        WHERE name IN ('{temp_names}')
    """)
    if (start + 500) % 5000 < 500:
        frappe.db.commit()
        print(f"  B: {min(start+500, len(items))}/{len(items)}")
frappe.db.commit()
print(f"  Phase B done [{time.time()-t0:.1f}s]")

frappe.db.sql('DROP TABLE IF EXISTS _cust_cpre_map')
frappe.db.commit()


# ═══════════════════════════════════════════════════════════════
# STEP 5: Set naming series — C-.############## → C-10000000034941+
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("STEP 5: NAMING SERIES")
print("=" * 60)

# Update Customer doctype naming_series options
frappe.db.sql("""
    UPDATE "tabDocField"
    SET options = 'C-.##############', "default" = 'C-.##############'
    WHERE parent = 'Customer' AND fieldname = 'naming_series'
""")
frappe.db.commit()
print("  naming_series options: C-.##############")

# Remove old C series (no dash), set new C- series
# Counter 10000000034940 means next = C-10000000034941
frappe.db.sql("DELETE FROM \"tabSeries\" WHERE name = 'C'")

series = frappe.db.sql("SELECT current FROM \"tabSeries\" WHERE name = 'C-'", as_dict=True)
if series:
    frappe.db.sql("UPDATE \"tabSeries\" SET current = 10000000034940 WHERE name = 'C-'")
else:
    frappe.db.sql("INSERT INTO \"tabSeries\" (name, current) VALUES ('C-', 10000000034940)")
frappe.db.commit()
print("  C- series counter: 10000000034940")
print("  Next new customer: C-10000000034941")


# ═══════════════════════════════════════════════════════════════
# STEP 6: Verify
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("VERIFICATION")
print("=" * 60)

total = frappe.db.sql('SELECT COUNT(*) FROM "tabCustomer"')[0][0]
with_c = frappe.db.sql("SELECT COUNT(*) FROM \"tabCustomer\" WHERE name LIKE 'C-%%'")[0][0]
without_c = total - with_c
print(f"  Customers total: {total}")
print(f"  With C- prefix: {with_c}")
print(f"  Without C- prefix: {without_c} (should be 0)")

# Samples
samples = frappe.db.sql("""
    SELECT name, customer_name, legacy_customer_id
    FROM "tabCustomer" ORDER BY name LIMIT 15
""", as_dict=True)
for s in samples:
    print(f"  {s['name']:30s}  {s.get('legacy_customer_id',''):20s}  {s['customer_name']}")

# Spot checks
for lid, label in [(4, "LPB4"), (13814, "Vegpro")]:
    c = frappe.db.sql('SELECT name, legacy_customer_id FROM "tabCustomer" WHERE legacy_account_id = %s', (lid,), as_dict=True)
    if c:
        print(f"  {label}: {c[0]['name']} (bank ref: {c[0].get('legacy_customer_id','')})")

# FK checks
for table, col in [("tabSales Invoice", "customer"), ("tabSubscription", "party"), ("tabIssue", "customer")]:
    orphans = frappe.db.sql(f"""
        SELECT COUNT(*) FROM "{table}" t
        WHERE t."{col}" IS NOT NULL AND t."{col}" != ''
        AND NOT EXISTS (SELECT 1 FROM "tabCustomer" c WHERE c.name = t."{col}")
    """)[0][0]
    print(f"  {table}.{col}: {'OK ✓' if orphans == 0 else f'ORPHANS: {orphans}'}")

frappe.clear_cache()
print("\nDone!")
