"""
Import missing devices into Service Equipment and enrich Service Locations
with fibre data (connection_type, OLT port, VLANs).

Run inside erpnext-backend-1:
  /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/import_devices_and_enrich.py
"""
import frappe
import pymysql
import os
import time
import hashlib
from datetime import datetime
from decimal import Decimal

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()
now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")

# ═══════════════════════════════════════════════════════════════
# LEGACY DB CONNECTION
# ═══════════════════════════════════════════════════════════════
conn = pymysql.connect(
    host=os.environ.get("LEGACY_HOST", "10.100.80.100"),
    user="facturation",
    password=os.environ.get("LEGACY_PW", ""),  # secret via env (jamais commité)
    database="gestionclient",
    cursorclass=pymysql.cursors.DictCursor
)

# ═══════════════════════════════════════════════════════════════
# PHASE 1: Build lookup maps
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 1: BUILD LOOKUP MAPS")
print("="*60)

# Map legacy delivery_id → ERPNext Service Location name
loc_map = {}
rows = 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)
for r in rows:
    loc_map[r["legacy_delivery_id"]] = r["name"]
print("Location map: {} entries".format(len(loc_map)))

# Map legacy account_id → ERPNext Customer name
cust_map = {}
rows = 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)
for r in rows:
    cust_map[r["legacy_account_id"]] = r["name"]
print("Customer map: {} entries".format(len(cust_map)))

# Get already-imported device IDs
existing_devices = set()
rows = frappe.db.sql("""
    SELECT legacy_device_id FROM "tabService Equipment"
    WHERE legacy_device_id IS NOT NULL AND legacy_device_id > 0
""")
for r in rows:
    existing_devices.add(r[0])
print("Already imported devices: {}".format(len(existing_devices)))

# Map delivery_id → account_id from legacy
with conn.cursor() as cur:
    cur.execute("SELECT id, account_id FROM delivery")
    delivery_account = {}
    for r in cur.fetchall():
        delivery_account[r["id"]] = r["account_id"]
print("Delivery→account map: {} entries".format(len(delivery_account)))

# ═══════════════════════════════════════════════════════════════
# PHASE 2: Import missing devices
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 2: IMPORT MISSING DEVICES")
print("="*60)

# Category mapping: legacy category → ERPNext equipment_type
CATEGORY_MAP = {
    "onu":              "ONT",
    "tplink_tplg":      "ONT",
    "tplink_device2":   "ONT",
    "raisecom_rcmg":    "ONT",
    "stb":              "Decodeur TV",
    "stb_ministra":     "Decodeur TV",
    "airosm":           "AP WiFi",
    "airos_ac":         "AP WiFi",
    "cambium":          "AP WiFi",
    "ht803g1ge":        "Telephone IP",
    "custom":           "Autre",
}

with conn.cursor() as cur:
    cur.execute("""
        SELECT id, delivery_id, category, name, manufacturier, model,
               sn, mac, manage, port, protocol, manage_cli, port_cli,
               protocol_cli, user, pass, parent
        FROM device ORDER BY id
    """)
    devices = cur.fetchall()

print("Total legacy devices: {}".format(len(devices)))

# Build set of existing serial numbers to avoid unique constraint violations
existing_serials = frappe.db.sql("""
    SELECT serial_number FROM "tabService Equipment" WHERE serial_number IS NOT NULL
""")
seen_serials = set(r[0] for r in existing_serials)
print("Existing serial numbers: {}".format(len(seen_serials)))

inserted = 0
skipped = 0
batch = []

for dev in devices:
    if dev["id"] in existing_devices:
        skipped += 1
        continue

    equipment_type = CATEGORY_MAP.get(dev["category"], "Autre")
    serial_number = dev["sn"] or "NO-SN-{}".format(dev["id"])
    # Ensure uniqueness — append device ID if serial already seen
    if serial_number in seen_serials:
        serial_number = "{}-D{}".format(serial_number, dev["id"])
    seen_serials.add(serial_number)
    mac = dev["mac"] or None
    brand = dev["manufacturier"] or None
    model = dev["model"] or None

    # Management IP — prefer manage_cli (clean IP), fallback to manage (may be URL)
    ip_address = None
    if dev["manage_cli"] and dev["manage_cli"].strip():
        ip_address = dev["manage_cli"].strip()
    elif dev["manage"] and dev["manage"].strip():
        mgmt = dev["manage"].strip()
        # If it's just an IP (not a full URL), use it
        if not mgmt.startswith("http") and not "/" in mgmt:
            ip_address = mgmt

    login_user = dev["user"] if dev["user"] and dev["user"].strip() else None
    login_pass = dev["pass"] if dev["pass"] and dev["pass"].strip() else None

    # Link to Service Location via delivery_id
    service_location = loc_map.get(dev["delivery_id"]) if dev["delivery_id"] else None

    # Link to Customer via delivery → account
    customer = None
    if dev["delivery_id"] and dev["delivery_id"] in delivery_account:
        account_id = delivery_account[dev["delivery_id"]]
        customer = cust_map.get(account_id)

    # Generate unique name
    eq_name = "EQ-{}".format(hashlib.md5(str(dev["id"]).encode()).hexdigest()[:10])

    batch.append({
        "name": eq_name,
        "now": now_str,
        "equipment_type": equipment_type,
        "serial_number": serial_number,
        "mac_address": mac,
        "brand": brand,
        "model": model,
        "ip_address": ip_address,
        "login_user": login_user,
        "login_password": login_pass,
        "customer": customer,
        "service_location": service_location,
        "legacy_device_id": dev["id"],
        "status": "Actif",
        "ownership": "Gigafibre",
    })
    inserted += 1

    # Insert in batches of 500
    if len(batch) >= 500:
        for eq in batch:
            frappe.db.sql("""
                INSERT INTO "tabService Equipment" (
                    name, creation, modified, modified_by, owner, docstatus, idx,
                    equipment_type, serial_number, mac_address, brand, model,
                    ip_address, login_user, login_password,
                    customer, service_location, legacy_device_id,
                    status, ownership
                ) VALUES (
                    %(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, 0,
                    %(equipment_type)s, %(serial_number)s, %(mac_address)s, %(brand)s, %(model)s,
                    %(ip_address)s, %(login_user)s, %(login_password)s,
                    %(customer)s, %(service_location)s, %(legacy_device_id)s,
                    %(status)s, %(ownership)s
                )
            """, eq)
        frappe.db.commit()
        batch = []
        print("  Inserted {}...".format(inserted))

# Final batch
if batch:
    for eq in batch:
        frappe.db.sql("""
            INSERT INTO "tabService Equipment" (
                name, creation, modified, modified_by, owner, docstatus, idx,
                equipment_type, serial_number, mac_address, brand, model,
                ip_address, login_user, login_password,
                customer, service_location, legacy_device_id,
                status, ownership
            ) VALUES (
                %(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, 0,
                %(equipment_type)s, %(serial_number)s, %(mac_address)s, %(brand)s, %(model)s,
                %(ip_address)s, %(login_user)s, %(login_password)s,
                %(customer)s, %(service_location)s, %(legacy_device_id)s,
                %(status)s, %(ownership)s
            )
        """, eq)
    frappe.db.commit()

print("Inserted {} new devices ({} already existed)".format(inserted, skipped))

if os.environ.get("SKIP_ENRICH", "0") == "1":
    print("SKIP_ENRICH=1 → devices importés, on saute l'enrichissement (Phase 3) + Phase 4")
    conn.close()
    import sys; sys.exit(0)

# ═══════════════════════════════════════════════════════════════
# PHASE 3: Enrich Service Locations with fibre data
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 3: ENRICH LOCATIONS WITH FIBRE DATA")
print("="*60)

with conn.cursor() as cur:
    # Get fibre data with service → delivery link (including OLT IP and name)
    cur.execute("""
        SELECT f.service_id, s.delivery_id,
               f.frame, f.slot, f.port, f.ontid,
               f.vlan_manage, f.vlan_internet, f.vlan_telephone, f.vlan_tele,
               f.sn as ont_sn, f.tech as fibre_tech,
               f.info_connect as olt_ip, fo.description as olt_name
        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
    """)
    fibre_data = cur.fetchall()

print("Fibre records with delivery link: {}".format(len(fibre_data)))

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

    # Build OLT port string: "OLT_IP frame/slot/port ONT:id" or "frame/slot/port ONT:id"
    olt_ip = fb.get("olt_ip") or ""
    olt_name = fb.get("olt_name") or ""
    frame = fb["frame"] if fb["frame"] is not None else 0
    slot = fb["slot"] if fb["slot"] is not None else 0
    port = fb["port"] if fb["port"] is not None else 0

    olt_port_parts = []
    if olt_ip:
        olt_port_parts.append(str(olt_ip))
    olt_port_parts.append("{}/{}/{}".format(frame, slot, port))
    if fb["ontid"]:
        olt_port_parts[-1] += " ONT:{}".format(fb["ontid"])
    olt_port = " ".join(olt_port_parts)

    vlans = []
    if fb["vlan_internet"]:
        vlans.append("inet:{}".format(fb["vlan_internet"]))
    if fb["vlan_manage"]:
        vlans.append("mgmt:{}".format(fb["vlan_manage"]))
    if fb["vlan_telephone"]:
        vlans.append("tel:{}".format(fb["vlan_telephone"]))
    if fb["vlan_tele"]:
        vlans.append("tv:{}".format(fb["vlan_tele"]))
    network_id = " ".join(vlans) if vlans else None

    # Build network_notes with OLT name if available
    network_notes = "OLT: {}".format(olt_name) if olt_name else None

    frappe.db.sql("""
        UPDATE "tabService Location"
        SET connection_type = 'Fibre FTTH',
            olt_port = %(olt_port)s,
            network_id = %(network_id)s,
            network_notes = COALESCE(%(network_notes)s, network_notes)
        WHERE name = %(name)s
          AND (connection_type IS NULL OR connection_type = '' OR connection_type = 'Fibre FTTH')
    """, {"name": loc_name, "olt_port": olt_port, "network_id": network_id, "network_notes": network_notes})
    updated_locs += 1

frappe.db.commit()
print("Enriched {} locations with fibre data".format(updated_locs))

# Also set connection_type for locations with wireless devices
with conn.cursor() as cur:
    cur.execute("""
        SELECT DISTINCT d.delivery_id
        FROM device d
        WHERE d.category IN ('airosm', 'airos_ac', 'cambium')
          AND d.delivery_id > 0
    """)
    wireless_deliveries = [r["delivery_id"] for r in cur.fetchall()]

wireless_updated = 0
for del_id in wireless_deliveries:
    loc_name = loc_map.get(del_id)
    if loc_name:
        frappe.db.sql("""
            UPDATE "tabService Location"
            SET connection_type = 'Sans-fil'
            WHERE name = %s
              AND (connection_type IS NULL OR connection_type = '')
        """, (loc_name,))
        wireless_updated += 1

frappe.db.commit()
print("Set {} locations as Sans-fil (wireless)".format(wireless_updated))

conn.close()

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

total_eq = frappe.db.sql('SELECT COUNT(*) FROM "tabService Equipment"')[0][0]
by_type = frappe.db.sql("""
    SELECT equipment_type, COUNT(*) as cnt FROM "tabService Equipment"
    GROUP BY equipment_type ORDER BY cnt DESC
""", as_dict=True)
print("Total Service Equipment: {}".format(total_eq))
for t in by_type:
    print("  {}: {}".format(t["equipment_type"], t["cnt"]))

with_customer = frappe.db.sql('SELECT COUNT(*) FROM "tabService Equipment" WHERE customer IS NOT NULL')[0][0]
with_location = frappe.db.sql('SELECT COUNT(*) FROM "tabService Equipment" WHERE service_location IS NOT NULL')[0][0]
with_ip = frappe.db.sql("SELECT COUNT(*) FROM \"tabService Equipment\" WHERE ip_address IS NOT NULL")[0][0]
with_creds = frappe.db.sql("SELECT COUNT(*) FROM \"tabService Equipment\" WHERE login_user IS NOT NULL")[0][0]
print("\nWith customer link: {}".format(with_customer))
print("With service_location link: {}".format(with_location))
print("With IP address: {}".format(with_ip))
print("With credentials: {}".format(with_creds))

# Location enrichment stats
loc_by_conn = frappe.db.sql("""
    SELECT connection_type, COUNT(*) as cnt FROM "tabService Location"
    GROUP BY connection_type ORDER BY cnt DESC
""", as_dict=True)
print("\nService Locations by connection type:")
for l in loc_by_conn:
    print("  {}: {}".format(l["connection_type"] or "(not set)", l["cnt"]))

with_olt = frappe.db.sql("SELECT COUNT(*) FROM \"tabService Location\" WHERE olt_port IS NOT NULL")[0][0]
print("With OLT port: {}".format(with_olt))

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