"""
Rename hex-based document IDs to human-readable names:
  - Customer:  CUST-{hex} → CUST-{legacy_account_id}
  - Service Location:  LOC-{hex} → "{address_line}, {city}" or LOC-{legacy_delivery_id}
  - Service Equipment: EQ-{hex}  → EQ-{legacy_device_id}

Uses direct SQL for speed (frappe.rename_doc is too slow for 15k+ records).
Updates all foreign key references.

Run inside erpnext-backend-1:
  /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/rename_to_readable_ids.py
"""
import frappe
import os
import time
import re
from datetime import datetime

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

T_TOTAL = time.time()

def batch_rename(table, old_to_new, ref_tables, label):
    """Rename documents and update all foreign key references."""
    if not old_to_new:
        print("  Nothing to rename for {}".format(label))
        return

    print("  Renaming {} {} records...".format(len(old_to_new), label))
    t0 = time.time()

    # Build temp mapping table for efficient bulk UPDATE
    # Process in batches to avoid memory issues
    batch_size = 2000
    items = list(old_to_new.items())

    for batch_start in range(0, len(items), batch_size):
        batch = items[batch_start:batch_start + batch_size]

        # Update main table name
        for old_name, new_name in batch:
            frappe.db.sql(
                'UPDATE "{}" SET name = %s WHERE name = %s'.format(table),
                (new_name, old_name)
            )

        # Update all foreign key references
        for ref_table, ref_col in ref_tables:
            for old_name, new_name in batch:
                frappe.db.sql(
                    'UPDATE "{}" SET {} = %s WHERE {} = %s'.format(ref_table, ref_col, ref_col),
                    (new_name, old_name)
                )

        frappe.db.commit()
        done = min(batch_start + batch_size, len(items))
        print("    {}/{}...".format(done, len(items)))

    elapsed = time.time() - t0
    print("  Done {} in {:.1f}s".format(label, elapsed))


# ═══════════════════════════════════════════════════════════════
# PHASE 1: RENAME CUSTOMERS
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 1: RENAME CUSTOMERS")
print("="*60)

customers = frappe.db.sql("""
    SELECT name, legacy_account_id FROM "tabCustomer"
    WHERE legacy_account_id IS NOT NULL AND legacy_account_id > 0
    ORDER BY legacy_account_id
""", as_dict=True)

cust_rename = {}
seen_cust = set()
for c in customers:
    new_name = "CUST-{}".format(c["legacy_account_id"])
    if new_name == c["name"]:
        continue  # already correct
    if new_name in seen_cust:
        new_name = "CUST-{}-b".format(c["legacy_account_id"])
    seen_cust.add(new_name)
    cust_rename[c["name"]] = new_name

print("Customers to rename: {} (of {})".format(len(cust_rename), len(customers)))

# All tables with a 'customer' Link field pointing to Customer
CUST_REFS = [
    ("tabService Location", "customer"),
    ("tabService Subscription", "customer"),
    ("tabService Equipment", "customer"),
    ("tabDispatch Job", "customer"),
    ("tabIssue", "customer"),
    ("tabSales Invoice", "customer"),
    ("tabSales Order", "customer"),
    ("tabDelivery Note", "customer"),
    ("tabSerial No", "customer"),
    ("tabProject", "customer"),
    ("tabWarranty Claim", "customer"),
    ("tabMaintenance Visit", "customer"),
    ("tabMaintenance Schedule", "customer"),
    ("tabLoyalty Point Entry", "customer"),
    ("tabPOS Invoice", "customer"),
    ("tabPOS Invoice Reference", "customer"),
    ("tabMaterial Request", "customer"),
    ("tabTimesheet", "customer"),
    ("tabBlanket Order", "customer"),
    ("tabDunning", "customer"),
    ("tabInstallation Note", "customer"),
    ("tabDelivery Stop", "customer"),
    ("tabPricing Rule", "customer"),
    ("tabTax Rule", "customer"),
    ("tabCall Log", "customer"),
    ("tabProcess Statement Of Accounts Customer", "customer"),
]

# Also update customer_name references in child tables where parent=customer name
CUST_PARENT_REFS = [
    ("tabHas Role", "parent"),
    ("tabDynamic Link", "link_name"),
]

all_cust_refs = CUST_REFS + CUST_PARENT_REFS

batch_rename("tabCustomer", cust_rename, all_cust_refs, "Customer")

# ═══════════════════════════════════════════════════════════════
# PHASE 2: RENAME SERVICE LOCATIONS
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 2: RENAME SERVICE LOCATIONS")
print("="*60)

locations = frappe.db.sql("""
    SELECT name, address_line, city, legacy_delivery_id
    FROM "tabService Location"
    WHERE legacy_delivery_id IS NOT NULL AND legacy_delivery_id > 0
    ORDER BY legacy_delivery_id
""", as_dict=True)

loc_rename = {}
seen_loc = set()
for loc in locations:
    addr = (loc["address_line"] or "").strip()
    city = (loc["city"] or "").strip()

    if addr and city:
        # Clean up address for use as document name
        new_name = "{}, {}".format(addr, city)
        # Frappe name max is 140 chars, keep it reasonable
        if len(new_name) > 120:
            new_name = new_name[:120]
        # Remove chars that cause issues in URLs
        new_name = new_name.replace("/", "-").replace("\\", "-")
    elif addr:
        new_name = addr[:120]
    else:
        new_name = "LOC-{}".format(loc["legacy_delivery_id"])

    # Handle duplicates (same address, different delivery)
    if new_name in seen_loc:
        new_name = "{} [{}]".format(new_name, loc["legacy_delivery_id"])
    seen_loc.add(new_name)

    if new_name != loc["name"]:
        loc_rename[loc["name"]] = new_name

print("Locations to rename: {} (of {})".format(len(loc_rename), len(locations)))

LOC_REFS = [
    ("tabService Subscription", "service_location"),
    ("tabService Equipment", "service_location"),
    ("tabDispatch Job", "service_location"),
    ("tabIssue", "service_location"),
]

batch_rename("tabService Location", loc_rename, LOC_REFS, "Service Location")

# ═══════════════════════════════════════════════════════════════
# PHASE 3: RENAME SERVICE EQUIPMENT
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 3: RENAME SERVICE EQUIPMENT")
print("="*60)

equipment = frappe.db.sql("""
    SELECT name, legacy_device_id FROM "tabService Equipment"
    WHERE legacy_device_id IS NOT NULL AND legacy_device_id > 0
    ORDER BY legacy_device_id
""", as_dict=True)

eq_rename = {}
seen_eq = set()
for eq in equipment:
    new_name = "EQ-{}".format(eq["legacy_device_id"])
    if new_name == eq["name"]:
        continue
    if new_name in seen_eq:
        new_name = "EQ-{}-b".format(eq["legacy_device_id"])
    seen_eq.add(new_name)
    eq_rename[eq["name"]] = new_name

print("Equipment to rename: {} (of {})".format(len(eq_rename), len(equipment)))

EQ_REFS = [
    ("tabService Subscription", "device"),
]

batch_rename("tabService Equipment", eq_rename, EQ_REFS, "Service Equipment")

# ═══════════════════════════════════════════════════════════════
# PHASE 4: VERIFY
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 4: VERIFY")
print("="*60)

# Sample customers
print("\nSample Customers:")
sample_c = frappe.db.sql("""
    SELECT name, customer_name FROM "tabCustomer"
    WHERE disabled = 0 ORDER BY name LIMIT 10
""", as_dict=True)
for c in sample_c:
    print("  {} → {}".format(c["name"], c["customer_name"]))

# Sample locations
print("\nSample Service Locations:")
sample_l = frappe.db.sql("""
    SELECT name, customer, city FROM "tabService Location"
    WHERE status = 'Active' ORDER BY name LIMIT 10
""", as_dict=True)
for l in sample_l:
    print("  {} → customer={}".format(l["name"], l["customer"]))

# Sample equipment
print("\nSample Service Equipment:")
sample_e = frappe.db.sql("""
    SELECT name, equipment_type, serial_number, customer FROM "tabService Equipment"
    ORDER BY name LIMIT 10
""", as_dict=True)
for e in sample_e:
    print("  {} → {} sn={} customer={}".format(
        e["name"], e["equipment_type"], e["serial_number"], e["customer"]))

# Check for orphaned references
print("\nOrphan check:")
orphan_sub = frappe.db.sql("""
    SELECT COUNT(*) FROM "tabService Subscription" ss
    WHERE ss.customer IS NOT NULL
      AND NOT EXISTS (SELECT 1 FROM "tabCustomer" c WHERE c.name = ss.customer)
""")[0][0]
print("  Subscriptions with invalid customer ref: {}".format(orphan_sub))

orphan_eq = frappe.db.sql("""
    SELECT COUNT(*) FROM "tabService Equipment" eq
    WHERE eq.service_location IS NOT NULL
      AND NOT EXISTS (SELECT 1 FROM "tabService Location" sl WHERE sl.name = eq.service_location)
""")[0][0]
print("  Equipment with invalid location ref: {}".format(orphan_eq))

frappe.clear_cache()
elapsed = time.time() - T_TOTAL
print("\n" + "="*60)
print("DONE in {:.1f}s — cache cleared".format(elapsed))
print("="*60)
