"""
migrate_missing_data.py — Populate the new custom fields from legacy DB.

Prerequisites: add_missing_custom_fields.py must have been run first.

Migrates:
  1. Customer: PPA, Stripe, VIP, termination, portal, flags
  2. Service Subscription: hijack details, dates, quotas
  3. Service Equipment: management access, parent hierarchy, legacy category
  4. Service Location: fibre detail (VLANs individual, OLT IP/name, ONT ID, etc.)
  5. Dispatch Job: tech times from bon_travail

Run inside erpnext-backend-1:
  /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/migrate_missing_data.py
"""
import frappe
import pymysql
import os
import json
import time
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)

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

def ts_to_date(ts):
    """Convert unix timestamp to YYYY-MM-DD, return None on failure."""
    if not ts or ts == 0:
        return None
    try:
        return datetime.fromtimestamp(int(ts)).strftime("%Y-%m-%d")
    except (ValueError, OSError, OverflowError):
        return None


# ═══════════════════════════════════════════════════════════════
# BUILD MAPS
# ═══════════════════════════════════════════════════════════════
print("Building lookup maps...")

# legacy_account_id → ERPNext Customer name
cust_map = {}
for r in frappe.db.sql('SELECT name, legacy_account_id FROM "tabCustomer" WHERE legacy_account_id IS NOT NULL AND legacy_account_id > 0', as_dict=True):
    cust_map[r["legacy_account_id"]] = r["name"]
print("  Customer map: {}".format(len(cust_map)))

# legacy_delivery_id → ERPNext Service Location name
loc_map = {}
for r in frappe.db.sql('SELECT name, legacy_delivery_id FROM "tabService Location" WHERE legacy_delivery_id IS NOT NULL AND legacy_delivery_id > 0', as_dict=True):
    loc_map[r["legacy_delivery_id"]] = r["name"]
print("  Location map: {}".format(len(loc_map)))

# legacy_service_id → ERPNext Service Subscription name
sub_map = {}
for r in frappe.db.sql('SELECT name, legacy_service_id FROM "tabService Subscription" WHERE legacy_service_id IS NOT NULL AND legacy_service_id > 0', as_dict=True):
    sub_map[r["legacy_service_id"]] = r["name"]
print("  Subscription map: {}".format(len(sub_map)))

# legacy_device_id → ERPNext Service Equipment name
dev_map = {}
for r in frappe.db.sql('SELECT name, legacy_device_id FROM "tabService Equipment" WHERE legacy_device_id IS NOT NULL AND legacy_device_id > 0', as_dict=True):
    dev_map[r["legacy_device_id"]] = r["name"]
print("  Device map: {}".format(len(dev_map)))


# ═══════════════════════════════════════════════════════════════
# 1. CUSTOMER — PPA, Stripe, VIP, termination, portal, flags
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("1. CUSTOMER — PPA, Stripe, VIP, termination, portal")
print("=" * 60)

with conn.cursor() as cur:
    cur.execute("""
        SELECT id, middle_name, title,
               ppa, ppa_name, ppa_code, ppa_branch, ppa_account,
               ppa_amount, ppa_amount_buffer, ppa_fixed, ppa_cc, ppa_all_invoice,
               stripe_id, stripe_ppa,
               vip, land_owner, pub, `call`,
               terminate_reason, terminate_cie, terminate_note, terminate_date,
               notes_client,
               username, password, keyword
        FROM account
    """)
    accounts = cur.fetchall()

updated = 0
for acct in accounts:
    cust_name = cust_map.get(acct["id"])
    if not cust_name:
        continue

    sets = []
    params = {"name": cust_name}

    # PPA fields
    if acct["ppa"]:
        sets.append("ppa_enabled = 1")
    for f in ["ppa_name", "ppa_code", "ppa_branch", "ppa_account"]:
        if acct.get(f):
            sets.append("{} = %({})s".format(f, f))
            params[f] = str(acct[f]).strip() if acct[f] else None
    if acct["ppa_amount"]:
        sets.append("ppa_amount = %(ppa_amount)s")
        params["ppa_amount"] = float(acct["ppa_amount"])
    if acct["ppa_amount_buffer"]:
        sets.append("ppa_amount_buffer = %(ppa_amount_buffer)s")
        params["ppa_amount_buffer"] = float(acct["ppa_amount_buffer"])
    if acct["ppa_fixed"]:
        sets.append("ppa_fixed = 1")
    if acct["ppa_cc"]:
        sets.append("ppa_cc = 1")
    if acct["ppa_all_invoice"]:
        sets.append("ppa_all_invoice = 1")

    # Stripe
    if acct["stripe_id"]:
        sets.append("stripe_customer_id = %(stripe_id)s")
        params["stripe_id"] = str(acct["stripe_id"]).strip()
    if acct["stripe_ppa"]:
        sets.append("stripe_ppa_enabled = 1")

    # Flags
    if acct["vip"]:
        sets.append("is_vip = 1")
    if acct["land_owner"]:
        sets.append("is_land_owner = 1")
    if acct["pub"]:
        sets.append("marketing_optin = 1")
    if acct.get("call"):
        sets.append("call_contact = 1")

    # Termination
    if acct["terminate_reason"]:
        sets.append("terminate_reason = %(terminate_reason)s")
        params["terminate_reason"] = str(acct["terminate_reason"])
    if acct["terminate_cie"]:
        sets.append("terminate_cie = %(terminate_cie)s")
        params["terminate_cie"] = str(acct["terminate_cie"])
    if acct["terminate_note"]:
        sets.append("terminate_note = %(terminate_note)s")
        params["terminate_note"] = str(acct["terminate_note"])
    if acct["terminate_date"]:
        td = ts_to_date(acct["terminate_date"])
        if td:
            sets.append("terminate_date = %(terminate_date)s")
            params["terminate_date"] = td

    # Client notes
    if acct["notes_client"]:
        sets.append("notes_client = %(notes_client)s")
        params["notes_client"] = str(acct["notes_client"])

    # Portal
    if acct["username"]:
        sets.append("portal_username = %(portal_username)s")
        params["portal_username"] = str(acct["username"])
    if acct["password"]:
        sets.append("portal_password_hash = %(portal_password_hash)s")
        params["portal_password_hash"] = str(acct["password"])

    # Misc
    if acct["middle_name"]:
        sets.append("middle_name = %(middle_name)s")
        params["middle_name"] = str(acct["middle_name"]).strip()
    if acct["title"]:
        sets.append("salutation_legacy = %(salutation_legacy)s")
        params["salutation_legacy"] = str(acct["title"]).strip()
    if acct["keyword"]:
        sets.append("search_keyword = %(search_keyword)s")
        params["search_keyword"] = str(acct["keyword"]).strip()

    if sets:
        sql = 'UPDATE "tabCustomer" SET {} WHERE name = %(name)s'.format(", ".join(sets))
        frappe.db.sql(sql, params)
        updated += 1

frappe.db.commit()
print("Updated {} customers".format(updated))


# ═══════════════════════════════════════════════════════════════
# 2. SERVICE SUBSCRIPTION — hijack details, dates, quotas
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("2. SERVICE SUBSCRIPTION — hijack, dates, IP")
print("=" * 60)

with conn.cursor() as cur:
    cur.execute("""
        SELECT id, hijack, hijack_desc,
               hijack_quota_day, hijack_quota_night,
               date_suspended, actif_until, date_next_invoice,
               forfait_internet, radius_conso, ip_fixe
        FROM service
    """)
    services = cur.fetchall()

updated = 0
for svc in services:
    sub_name = sub_map.get(svc["id"])
    if not sub_name:
        continue

    sets = []
    params = {"name": sub_name}

    if svc["hijack"]:
        sets.append("is_custom_pricing = 1")
    if svc["hijack_desc"]:
        sets.append("custom_pricing_desc = %(hijack_desc)s")
        params["hijack_desc"] = str(svc["hijack_desc"])
    if svc["hijack_quota_day"]:
        sets.append("quota_day_gb = %(quota_day)s")
        params["quota_day"] = float(svc["hijack_quota_day"])
    if svc["hijack_quota_night"]:
        sets.append("quota_night_gb = %(quota_night)s")
        params["quota_night"] = float(svc["hijack_quota_night"])

    ds = ts_to_date(svc["date_suspended"])
    if ds:
        sets.append("date_suspended = %(date_suspended)s")
        params["date_suspended"] = ds
    au = ts_to_date(svc["actif_until"])
    if au:
        sets.append("active_until = %(active_until)s")
        params["active_until"] = au
    ni = ts_to_date(svc["date_next_invoice"])
    if ni:
        sets.append("next_invoice_date = %(next_invoice_date)s")
        params["next_invoice_date"] = ni

    if svc["forfait_internet"]:
        sets.append("forfait_internet = 1")
    if svc["radius_conso"]:
        sets.append("radius_consumption = %(radius_conso)s")
        params["radius_conso"] = str(svc["radius_conso"])
    if svc.get("ip_fixe"):
        sets.append("static_ip = %(static_ip)s")
        params["static_ip"] = str(svc["ip_fixe"])

    if sets:
        sql = 'UPDATE "tabService Subscription" SET {} WHERE name = %(name)s'.format(", ".join(sets))
        frappe.db.sql(sql, params)
        updated += 1

frappe.db.commit()
print("Updated {} subscriptions".format(updated))


# ═══════════════════════════════════════════════════════════════
# 3. SERVICE EQUIPMENT — management, parent, category, device_attr
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("3. SERVICE EQUIPMENT — management, parent, category")
print("=" * 60)

with conn.cursor() as cur:
    cur.execute("""
        SELECT id, manage, port, protocol, manage_cli, port_cli, protocol_cli,
               parent, category, name as device_name
        FROM device
    """)
    devices = cur.fetchall()

updated = 0
for dev in devices:
    eq_name = dev_map.get(dev["id"])
    if not eq_name:
        continue

    sets = []
    params = {"name": eq_name}

    if dev["manage"]:
        sets.append("manage_url = %(manage_url)s")
        params["manage_url"] = str(dev["manage"]).strip()
    if dev["port"]:
        sets.append("manage_port = %(manage_port)s")
        params["manage_port"] = int(dev["port"])
    if dev["protocol"]:
        proto = str(dev["protocol"]).strip().upper()
        if proto in ("HTTP", "HTTPS", "SSH", "TELNET", "SNMP"):
            sets.append("manage_protocol = %(manage_protocol)s")
            params["manage_protocol"] = proto
    if dev["manage_cli"]:
        sets.append("cli_ip = %(cli_ip)s")
        params["cli_ip"] = str(dev["manage_cli"]).strip()
    if dev["port_cli"]:
        sets.append("cli_port = %(cli_port)s")
        params["cli_port"] = int(dev["port_cli"])
    if dev["protocol_cli"]:
        proto_cli = str(dev["protocol_cli"]).strip().upper()
        if proto_cli in ("SSH", "TELNET"):
            sets.append("cli_protocol = %(cli_protocol)s")
            params["cli_protocol"] = proto_cli
    if dev["parent"]:
        parent_eq = dev_map.get(dev["parent"])
        if parent_eq:
            sets.append("parent_device = %(parent_device)s")
            params["parent_device"] = parent_eq
    if dev["category"]:
        sets.append("legacy_category = %(legacy_category)s")
        params["legacy_category"] = str(dev["category"])
    if dev["device_name"]:
        sets.append("device_name_legacy = %(device_name_legacy)s")
        params["device_name_legacy"] = str(dev["device_name"])

    if sets:
        sql = 'UPDATE "tabService Equipment" SET {} WHERE name = %(name)s'.format(", ".join(sets))
        frappe.db.sql(sql, params)
        updated += 1

frappe.db.commit()
print("Updated {} devices".format(updated))

# ── device_attr → JSON MAC addresses + stb_id ──
print("\nImporting device_attr...")
with conn.cursor() as cur:
    cur.execute("SELECT device_id, `key`, value FROM device_attr ORDER BY device_id")
    attrs = cur.fetchall()

# Group by device_id
device_attrs = {}
for attr in attrs:
    did = attr["device_id"]
    if did not in device_attrs:
        device_attrs[did] = {}
    device_attrs[did][attr["key"]] = attr["value"]

updated_attrs = 0
for did, kv in device_attrs.items():
    eq_name = dev_map.get(did)
    if not eq_name:
        continue

    sets = []
    params = {"name": eq_name}

    # Extract MAC addresses
    macs = {}
    stb_id = None
    for k, v in kv.items():
        if k.startswith("mac_") or k.startswith("eth"):
            macs[k] = v
        if k == "stb_id":
            stb_id = v

    if macs:
        sets.append("mac_addresses_json = %(macs_json)s")
        params["macs_json"] = json.dumps(macs)
    if stb_id:
        sets.append("iptv_subscription_id = %(stb_id)s")
        params["stb_id"] = str(stb_id)

    if sets:
        sql = 'UPDATE "tabService Equipment" SET {} WHERE name = %(name)s'.format(", ".join(sets))
        frappe.db.sql(sql, params)
        updated_attrs += 1

frappe.db.commit()
print("Updated {} devices with attrs".format(updated_attrs))


# ═══════════════════════════════════════════════════════════════
# 4. SERVICE LOCATION — fibre detail (VLANs, OLT IP, ONT, etc.)
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("4. SERVICE LOCATION — fibre detail, VLANs, building")
print("=" * 60)

with conn.cursor() as cur:
    cur.execute("""
        SELECT f.id as fibre_id, f.service_id, f.sn as ont_sn,
               f.info_connect as olt_ip, f.ontid, f.tech as terrain,
               f.distance, f.nb_portees, f.temps_estim, f.suite,
               f.boitier_pas_install,
               f.vlan_manage, f.vlan_internet, f.vlan_telephone, f.vlan_tele,
               f.manage_service_id, f.internet_service_id,
               f.telephone_service_id, f.tele_service_id,
               f.placemarks_id, f.appartements_id,
               fo.description as olt_name,
               s.delivery_id
        FROM fibre f
        LEFT JOIN fibre_olt fo ON f.info_connect = fo.ip
        LEFT JOIN service s ON f.service_id = s.id
        WHERE s.delivery_id IS NOT NULL
    """)
    fibres = cur.fetchall()

updated = 0
for fb in fibres:
    loc_name = loc_map.get(fb["delivery_id"])
    if not loc_name:
        continue

    sets = []
    params = {"name": loc_name}

    if fb["fibre_id"]:
        sets.append("legacy_fibre_id = %(fibre_id)s")
        params["fibre_id"] = fb["fibre_id"]
    if fb["ont_sn"]:
        sets.append("ont_serial = %(ont_sn)s")
        params["ont_sn"] = str(fb["ont_sn"])
    if fb["olt_ip"]:
        sets.append("olt_ip = %(olt_ip)s")
        params["olt_ip"] = str(fb["olt_ip"])
    if fb["olt_name"]:
        sets.append("olt_name = %(olt_name)s")
        params["olt_name"] = str(fb["olt_name"])
    if fb["ontid"]:
        sets.append("ont_id = %(ontid)s")
        params["ontid"] = int(fb["ontid"])
    if fb["terrain"]:
        sets.append("terrain_type = %(terrain)s")
        params["terrain"] = str(fb["terrain"])
    if fb["distance"]:
        sets.append("fibre_distance_m = %(distance)s")
        params["distance"] = float(fb["distance"])
    if fb["nb_portees"]:
        sets.append("fibre_spans = %(spans)s")
        params["spans"] = int(fb["nb_portees"])
    if fb["temps_estim"]:
        sets.append("install_time_estimate = %(temps)s")
        params["temps"] = str(fb["temps_estim"])
    if fb["suite"]:
        sets.append("is_apartment = 1")
    if fb["boitier_pas_install"]:
        sets.append("box_not_installed = 1")

    # VLANs — individual columns
    if fb["vlan_manage"]:
        sets.append("vlan_manage = %(vlan_manage)s")
        params["vlan_manage"] = int(fb["vlan_manage"])
    if fb["vlan_internet"]:
        sets.append("vlan_internet = %(vlan_internet)s")
        params["vlan_internet"] = int(fb["vlan_internet"])
    if fb["vlan_telephone"]:
        sets.append("vlan_telephone = %(vlan_telephone)s")
        params["vlan_telephone"] = int(fb["vlan_telephone"])
    if fb["vlan_tele"]:
        sets.append("vlan_tv = %(vlan_tv)s")
        params["vlan_tv"] = int(fb["vlan_tele"])

    # Legacy service IDs
    for src, dst in [("manage_service_id", "manage_service_id"),
                     ("internet_service_id", "internet_service_id"),
                     ("telephone_service_id", "telephone_service_id"),
                     ("tele_service_id", "tv_service_id")]:
        if fb[src]:
            sets.append("{} = %({})s".format(dst, dst))
            params[dst] = int(fb[src])

    if fb["placemarks_id"]:
        sets.append("placemarks_id = %(placemarks_id)s")
        params["placemarks_id"] = str(fb["placemarks_id"])
    if fb["appartements_id"]:
        sets.append("apartment_building_id = %(apt_id)s")
        params["apt_id"] = str(fb["appartements_id"])

    if sets:
        sql = 'UPDATE "tabService Location" SET {} WHERE name = %(name)s'.format(", ".join(sets))
        frappe.db.sql(sql, params)
        updated += 1

frappe.db.commit()
print("Updated {} locations with fibre detail".format(updated))

# Also import delivery.address2 and delivery.note
with conn.cursor() as cur:
    cur.execute("SELECT id, address2, note FROM delivery WHERE address2 IS NOT NULL OR note IS NOT NULL")
    deliveries = cur.fetchall()

updated_del = 0
for d in deliveries:
    loc_name = loc_map.get(d["id"])
    if not loc_name:
        continue
    sets = []
    params = {"name": loc_name}
    if d["address2"]:
        sets.append("address_line_2 = %(addr2)s")
        params["addr2"] = str(d["address2"]).strip()
    if d["note"]:
        sets.append("delivery_notes = %(note)s")
        params["note"] = str(d["note"])
    if sets:
        sql = 'UPDATE "tabService Location" SET {} WHERE name = %(name)s'.format(", ".join(sets))
        frappe.db.sql(sql, params)
        updated_del += 1

frappe.db.commit()
print("Updated {} locations with address2/notes".format(updated_del))


# ═══════════════════════════════════════════════════════════════
# 5. DONE
# ═══════════════════════════════════════════════════════════════
conn.close()
print("\n" + "=" * 60)
print("MIGRATION COMPLETE")
print("=" * 60)
print("Next: run migrate_provisioning_data.py for WiFi/VoIP from GenieACS")
