"""Compare PPPoE active sessions from climax MikroTik against Gaiia accounts.

Checks:
  1. Is the PPPoE username found as a Gaiia account?
  2. Does the Gaiia account's inventory item IP match the PPPoE IP?

Usage: cd /Users/graham/dev/network/gaiia && uv run python compare_pppoe.py
"""

from __future__ import annotations

import os
import sys
from typing import Any

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
from gaiia.client import GaiiaClient

# PPPoE active sessions from climax (2026-07-02)
PPPOE_SESSIONS: list[dict[str, str]] = [
    {"name": "charlesboone", "address": "100.64.7.247", "mac": "C8:9E:43:E3:99:EA"},
    {"name": "amberprater", "address": "100.64.7.243", "mac": "6C:CD:D6:2C:31:94"},
    {"name": "matthewgoodwin", "address": "100.64.7.241", "mac": "34:98:B5:44:42:A4"},
    {"name": "robbymccollom", "address": "100.64.7.239", "mac": "D8:B3:70:92:0C:C6"},
    {"name": "gordonhamilton", "address": "100.64.7.237", "mac": "A0:63:91:3A:29:B1"},
    {"name": "chasewilliams", "address": "100.64.7.235", "mac": "38:94:ED:5B:82:E9"},
    {"name": "cynthiajones", "address": "100.64.7.233", "mac": "E4:FA:C4:56:CA:D9"},
    {"name": "janicealexander", "address": "100.64.7.225", "mac": "E0:D3:62:E8:65:C5"},
    {"name": "michaelray", "address": "204.110.188.38", "mac": "9C:05:D6:59:FF:63"},
    {"name": "marysmelser", "address": "100.64.7.218", "mac": "00:01:9F:33:54:B9"},
    {"name": "eddieyarbrough", "address": "100.64.7.216", "mac": "10:C3:7B:42:D8:B8"},
    {"name": "timgilbert", "address": "100.64.7.213", "mac": "94:A6:7E:EE:98:B8"},
    {"name": "chadwhitsell", "address": "100.64.7.211", "mac": "04:95:E6:76:3C:91"},
    {"name": "donnacampbell", "address": "100.64.7.209", "mac": "74:FE:CE:3A:6D:AC"},
    {"name": "bryangoulart", "address": "100.64.7.205", "mac": "8C:90:2D:E9:90:7A"},
    {"name": "crystalharney", "address": "100.64.7.201", "mac": "98:25:4A:04:B6:43"},
    {"name": "johnvayo", "address": "100.64.7.197", "mac": "98:25:4A:04:B5:89"},
    {"name": "richardbarragan", "address": "100.64.7.193", "mac": "8C:90:2D:E9:98:E1"},
    {"name": "carolstrickland", "address": "100.64.7.189", "mac": "E8:DA:00:18:54:45"},
    {"name": "timbagert", "address": "100.64.7.140", "mac": "74:FE:CE:3A:6D:8E"},
    {"name": "douggarber", "address": "100.64.7.136", "mac": "00:01:9F:33:79:A9"},
    {"name": "janetkern", "address": "100.64.7.61", "mac": "28:C6:8E:C1:06:AF"},
    {"name": "gregmcintire", "address": "100.64.7.59", "mac": "66:62:8B:10:10:65"},
    {"name": "jenniferboon", "address": "100.64.7.35", "mac": "98:25:4A:04:BA:EA"},
    {"name": "glendabeauchamp", "address": "100.64.7.15", "mac": "8C:90:2D:E9:97:85"},
    {"name": "jmichaelculverhouse", "address": "100.64.7.250", "mac": "34:98:B5:31:D3:3A"},
    {"name": "elviraquezada", "address": "100.64.7.249", "mac": "E0:C2:50:47:DC:1A"},
    {"name": "tammieventris", "address": "100.64.7.232", "mac": "98:25:4A:04:A5:E1"},
    {"name": "taekim", "address": "204.110.188.42", "mac": "9C:5C:8E:44:85:B4"},
    {"name": "ruthfengler", "address": "100.64.7.246", "mac": "A0:63:91:25:F6:E5"},
    {"name": "buddyswan", "address": "100.64.7.245", "mac": "00:01:9F:34:D2:21"},
    {"name": "rhondabolton", "address": "100.64.7.231", "mac": "A0:04:60:94:D5:FF"},
]

# Known PPPoE-name → Gaiia-displayName mismatches
MANUAL_MAP: dict[str, str] = {
    "bryangoulart": "Brian Goulart",  # typo in PPPoE username
}


def flatten(name: str) -> str:
    """Lowercase and remove spaces/punctuation from a name."""
    return name.lower().replace(" ", "").replace("-", "").replace("'", "")


def fetch_all_accounts(client: GaiiaClient) -> list[dict[str, Any]]:
    """Fetch all Gaiia accounts (id, readableId, displayName)."""
    query = """
    query($after: String) {
      accounts(first: 50, after: $after) {
        nodes { id readableId displayName }
        pageInfo { hasNextPage endCursor }
        totalCount
      }
    }
    """
    accounts: list[dict[str, Any]] = []
    cursor: str | None = None
    has_next = True
    while has_next:
        variables: dict[str, Any] = {}
        if cursor:
            variables["after"] = cursor
        data = client.query(query, variables)
        conn = data["accounts"]
        accounts.extend(conn.get("nodes") or [])
        has_next = conn["pageInfo"]["hasNextPage"]
        cursor = conn["pageInfo"].get("endCursor")
        print(f"  Fetched {len(accounts)}/{conn.get('totalCount', '?')} accounts...",
              file=sys.stderr)
    return accounts


def fetch_account_inventory(
    client: GaiiaClient, account_id: str
) -> list[dict[str, Any]]:
    """Fetch inventory items for a specific account, including IP addresses."""
    query = """
    query($id: GlobalID!) {
      account(id: $id) {
        assignedInventoryItems(first: 10) {
          nodes {
            inventoryItem {
              id
              ipAddressV4
              ipAddressV6
              model {
                name
                manufacturer { name }
              }
            }
          }
        }
      }
    }
    """
    data = client.query(query, {"id": account_id})
    acct = data.get("account")
    if not acct:
        return []
    items = acct.get("assignedInventoryItems", {})
    assignations = items.get("nodes") or []
    # Extract the actual inventory items from assignations
    result = []
    for assign in assignations:
        item = assign.get("inventoryItem")
        if item:
            result.append(item)
    return result


def main() -> None:
    api_key = os.environ.get("GAIIA_KEY")
    if not api_key:
        print("Error: GAIIA_KEY env var not set", file=sys.stderr)
        sys.exit(1)

    client = GaiiaClient(api_key, timezone="America/Chicago")

    print("Fetching all accounts from Gaiia...", file=sys.stderr)
    accounts = fetch_all_accounts(client)
    print(f"Total: {len(accounts)} accounts\n", file=sys.stderr)

    # Build lookup: flattened displayName → account
    by_flat: dict[str, dict[str, Any]] = {}
    for a in accounts:
        by_flat[flatten(a.get("displayName", ""))] = a

    # --- Step 1: Match PPPoE names to Gaiia accounts ---
    print("=" * 75)
    print("CLIMAX PPPoE → GAIIA ACCOUNT MATCHING")
    print("=" * 75)

    matched: list[dict[str, Any]] = []
    unmatched: list[dict[str, Any]] = []

    for session in PPPOE_SESSIONS:
        pname = session["name"]
        flat = flatten(pname)
        account = by_flat.get(flat)

        # Try manual map
        if not account and pname in MANUAL_MAP:
            mapped_flat = flatten(MANUAL_MAP[pname])
            account = by_flat.get(mapped_flat)

        session["_account"] = account
        if account:
            matched.append(session)
        else:
            unmatched.append(session)

    print(f"{'PPPoE':<25} {'PPPoE IP':<18} {'Gaiia Name':<25} {'Account ID':<8}")
    print("-" * 78)
    for s in matched:
        acc = s["_account"]
        print(f"{s['name']:<25} {s['address']:<18} {acc['displayName']:<25} {acc['readableId']:<8}")
    for s in unmatched:
        print(f"{s['name']:<25} {s['address']:<18} {'(not in Gaiia)':<25} {'-':<8}")

    print(f"\nMatched: {len(matched)} | Not in Gaiia: {len(unmatched)}")
    if unmatched:
        print("Not in Gaiia:")
        for s in unmatched:
            print(f"  - {s['name']} ({s['address']})")

    # --- Step 2: Compare IP addresses from inventory items ---
    print(f"\n{'=' * 75}")
    print("IP ADDRESS COMPARISON (PPPoE active IP vs Gaiia inventory item IP)")
    print("=" * 75)

    ip_matched = 0
    ip_mismatched = 0
    ip_no_inventory = 0
    ip_not_checked = 0

    for s in matched:
        acc = s["_account"]
        pppoe_ip = s["address"]
        pppoe_name = s["name"]

        # Fetch inventory items for this account
        items = fetch_account_inventory(client, acc["id"])
        s["_items"] = items

        if not items:
            print(f"{pppoe_name:<25} {pppoe_ip:<18} (no inventory items in Gaiia)")
            ip_no_inventory += 1
            continue

        # Check if any inventory item has an IP matching the PPPoE IP
        found_match = False
        for item in items:
            item_ip = item.get("ipAddressV4")
            if item_ip == pppoe_ip:
                model = item.get("model", {})
                model_name = model.get("name", "?") if model else "?"
                print(f"{pppoe_name:<25} {pppoe_ip:<18} MATCH  (item: {model_name})")
                ip_matched += 1
                found_match = True
                break

        if not found_match:
            item_ips = [i.get("ipAddressV4") for i in items if i.get("ipAddressV4")]
            if item_ips:
                print(f"{pppoe_name:<25} {pppoe_ip:<18} MISMATCH  (Gaiia has: {', '.join(item_ips)})")
                ip_mismatched += 1
            else:
                print(f"{pppoe_name:<25} {pppoe_ip:<18} NO IP SET  (has inventory items but no IPs)")
                ip_no_inventory += 1

    print(f"\n--- Summary ---")
    print(f"Accounts in Gaiia:             {len(matched)}/{len(PPPOE_SESSIONS)}")
    print(f"Not in Gaiia:                  {len(unmatched)}")
    print(f"IP matches PPPoE:              {ip_matched}")
    print(f"IP mismatch (different IP):    {ip_mismatched}")
    print(f"No inventory / no IP in Gaiia: {ip_no_inventory}")

    if unmatched:
        print(f"\nAccounts missing from Gaiia (need investigation):")
        for s in unmatched:
            mac = s.get("mac", "")
            print(f"  {s['name']}  IP={s['address']}  MAC={mac}")

    client.close()


if __name__ == "__main__":
    main()
