"""
Fix income_account using pure SQL bulk updates via a temp mapping table.
Much faster than row-by-row.

Run inside erpnext-backend-1:
  /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/fix_income_sql.py
"""
import frappe
import pymysql
import os, sys, time

sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)

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

DEFAULT_INCOME = "Autres produits d'exploitation - T"

# ── Step 1: Build SKU → income account ──
print("\n=== Step 1: Build SKU → GL mapping ===")
legacy = pymysql.connect(
    host="legacy-db", user="facturation", password="***LEGACY-DB-PW-REDACTED***",
    database="gestionclient", cursorclass=pymysql.cursors.DictCursor
)
with legacy.cursor() as cur:
    cur.execute("""SELECT p.sku, pc.num_compte FROM product p
                   JOIN product_cat pc ON p.category = pc.id
                   WHERE p.sku IS NOT NULL AND p.sku != '' AND pc.num_compte IS NOT NULL""")
    sku_to_gl_num = {r['sku']: str(int(r['num_compte'])) for r in cur.fetchall() if r['num_compte']}

accts = frappe.db.sql("""SELECT account_number, name FROM "tabAccount"
    WHERE root_type IN ('Income','Expense') AND company='TARGO' AND is_group=0
    AND account_number IS NOT NULL AND account_number != ''""")
gl_by_number = {r[0]: r[1] for r in accts}
sku_to_income = {sku: gl_by_number[num] for sku, num in sku_to_gl_num.items() if num in gl_by_number}
print(f"  {len(sku_to_income)} SKU → account mappings")

# ── Step 2: Load legacy items and build (invoice_id, row_num) → account ──
print("\n=== Step 2: Load legacy items ===")
t0 = time.time()
with legacy.cursor() as cur:
    cur.execute("""
        SELECT invoice_id, sku,
               ROW_NUMBER() OVER (PARTITION BY invoice_id ORDER BY id) as rn
        FROM invoice_item
        ORDER BY invoice_id, id
    """)
    legacy_items = cur.fetchall()
legacy.close()
print(f"  {len(legacy_items)} legacy items [{time.time()-t0:.0f}s]")

# ── Step 3: Create temp mapping table in PostgreSQL ──
print("\n=== Step 3: Create temp mapping table ===")
t0 = time.time()

# Drop if exists
frappe.db.sql("DROP TABLE IF EXISTS _tmp_sku_income_map")
frappe.db.commit()

# Create temp table: (legacy_invoice_id, idx, income_account)
frappe.db.sql("""
    CREATE TABLE _tmp_sku_income_map (
        legacy_invoice_id bigint,
        idx integer,
        income_account varchar(140),
        PRIMARY KEY (legacy_invoice_id, idx)
    )
""")
frappe.db.commit()

# Insert mappings in batches
batch = []
batch_size = 5000
inserted = 0

for li in legacy_items:
    sku = li['sku'] or ''
    acct = sku_to_income.get(sku)
    if acct:
        batch.append((li['invoice_id'], li['rn'], acct))
        if len(batch) >= batch_size:
            args = ','.join(['(%s,%s,%s)'] * len(batch))
            flat = [v for t in batch for v in t]
            frappe.db.sql(f"INSERT INTO _tmp_sku_income_map VALUES {args}", flat)
            frappe.db.commit()
            inserted += len(batch)
            batch = []

if batch:
    args = ','.join(['(%s,%s,%s)'] * len(batch))
    flat = [v for t in batch for v in t]
    frappe.db.sql(f"INSERT INTO _tmp_sku_income_map VALUES {args}", flat)
    frappe.db.commit()
    inserted += len(batch)

print(f"  Inserted {inserted} mappings [{time.time()-t0:.0f}s]")

# Add index
frappe.db.sql("CREATE INDEX idx_tmp_map_lid ON _tmp_sku_income_map (legacy_invoice_id)")
frappe.db.commit()

# ── Step 4: Bulk UPDATE Sales Invoice Items via JOIN ──
print("\n=== Step 4: Bulk update Sales Invoice Items ===")
t0 = time.time()

result = frappe.db.sql("""
    UPDATE "tabSales Invoice Item" sii
    SET income_account = m.income_account
    FROM "tabSales Invoice" si, _tmp_sku_income_map m
    WHERE si.name = sii.parent
      AND m.legacy_invoice_id = si.legacy_invoice_id
      AND m.idx = sii.idx
      AND sii.income_account = %s
""", (DEFAULT_INCOME,))
frappe.db.commit()
print(f"  Update done [{time.time()-t0:.0f}s]")

# Check results
dist = frappe.db.sql("""SELECT income_account, COUNT(*) FROM "tabSales Invoice Item"
    GROUP BY income_account ORDER BY COUNT(*) DESC""")
print("  Income account distribution:")
for row in dist:
    print(f"    {row[1]:>10}  {row[0]}")

# ── Step 5: Update GL Entries ──
print("\n=== Step 5: Update GL Entries ===")
t0 = time.time()

# For each invoice, get the dominant income account from its items
# and update the GL entry
print("  Getting dominant account per invoice...")
dom = frappe.db.sql("""
    SELECT parent, income_account, SUM(ABS(base_net_amount)) as total
    FROM "tabSales Invoice Item"
    WHERE income_account != %s
    GROUP BY parent, income_account
""", (DEFAULT_INCOME,))

# Find dominant per invoice
inv_best = {}
for inv_name, acct, total in dom:
    total = float(total or 0)
    if inv_name not in inv_best or total > inv_best[inv_name][1]:
        inv_best[inv_name] = (acct, total)

print(f"  {len(inv_best)} invoices with mapped accounts")

# Batch update GL entries
by_acct = {}
for inv_name, (acct, _) in inv_best.items():
    by_acct.setdefault(acct, []).append(inv_name)

gl_updated = 0
for acct_name, inv_names in by_acct.items():
    for i in range(0, len(inv_names), 1000):
        batch = inv_names[i:i+1000]
        placeholders = ','.join(['%s'] * len(batch))
        frappe.db.sql(
            f"""UPDATE "tabGL Entry" SET account = %s
                WHERE voucher_type = 'Sales Invoice'
                AND voucher_no IN ({placeholders})
                AND account = %s""",
            [acct_name] + batch + [DEFAULT_INCOME]
        )
        frappe.db.commit()
    gl_updated += len(inv_names)
    print(f"    {acct_name}: {len(inv_names)} invoices")

print(f"  Total: {gl_updated} invoices [{time.time()-t0:.0f}s]")

# ── Step 6: Cleanup and verify ──
print("\n=== Step 6: Verify ===")
frappe.db.sql("DROP TABLE IF EXISTS _tmp_sku_income_map")
frappe.db.commit()

r = frappe.db.sql("""
    SELECT account, COUNT(*) as cnt, ROUND(SUM(credit)::numeric, 2) as total_credit
    FROM "tabGL Entry"
    WHERE voucher_type = 'Sales Invoice' AND credit > 0
    GROUP BY account ORDER BY cnt DESC LIMIT 25
""")
print("  GL credit entries by account (Sales Invoice):")
for row in r:
    print(f"    {row[1]:>10}  ${row[2]:>14}  {row[0]}")

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