#!/usr/bin/env python3
"""
Clear all MAC->IP address assignments from customer inventory items.

Two-phase approach:
  1. List all active IP assignments via paginated V1 public API
  2. Expire each one via startWorkflowExecution mutation

Before running:
  1. Create the "Expire Single IP Assignment" workflow in Gaiia UI
     (use expire_one_workflow.json)
  2. Copy its workflow ID and set EXPIRE_WF_ID below
  3. source .envrc
  4. uv run python3 clear_all_ips.py --dry-run   (preview)
  5. uv run python3 clear_all_ips.py              (execute)
"""

from gaiia import GaiiaClient, GaiiaMutationError
import json
import os
import sys
import time

EXPIRE_WF_ID = os.environ.get("EXPIRE_WF_ID", "")

def list_all_assignments(client):
    """Paginate through all inventory items and return active MAC->IP pairs."""
    all_assignments = []
    after = None
    page = 0

    while True:
        page += 1
        query = """
        query Q($after: String) {
            inventoryItems(
                filters: {assigneeType: {equals: ACCOUNT}}
                first: 250
                after: $after
            ) {
                nodes {
                    ipAddressAssignments {
                        nodes { ipAddress macAddress isActive }
                    }
                }
                pageInfo { hasNextPage endCursor }
                totalCount
            }
        }
        """
        data = client.query(query, {"after": after} if after else {})
        result = data['inventoryItems']

        if page == 1:
            print(f"Total inventory items: {result['totalCount']}")

        for item in result['nodes']:
            for a in item.get('ipAddressAssignments', {}).get('nodes', []):
                if a.get('isActive') != False:
                    all_assignments.append({
                        'macAddress': a['macAddress'],
                        'ipAddress': a['ipAddress']
                    })

        page_info = result['pageInfo']
        sys.stderr.write(f"\r  Page {page}: {len(all_assignments)} assignments so far...")
        sys.stderr.flush()

        if not page_info.get('hasNextPage'):
            break
        after = page_info['endCursor']

    sys.stderr.write("\n")
    return all_assignments


def expire_one(client, mac_address, ip_address, dry_run=False):
    """Call the Expire Single IP Assignment workflow via startWorkflowExecution."""
    if dry_run:
        print(f"  [DRY-RUN] {mac_address} -> {ip_address}")
        return True

    mutation = """
    mutation Expire($input: StartWorkflowExecutionInput!) {
        startWorkflowExecution(input: $input) {
            workflowExecution { id status }
        }
    }
    """
    try:
        client.query(mutation, {
            "input": {
                "workflowId": EXPIRE_WF_ID,
                "input": {"macAddress": mac_address, "ipAddress": ip_address}
            }
        })
        return True
    except GaiiaMutationError as e:
        print(f"  FAILED {mac_address} -> {ip_address}: {e}")
        return False


def main():
    if not EXPIRE_WF_ID:
        print("ERROR: Set EXPIRE_WF_ID env var to the workflow ID of 'Expire Single IP Assignment'")
        print("Example: EXPIRE_WF_ID=workflow_xxx uv run python3 clear_all_ips.py --dry-run")
        sys.exit(1)

    dry_run = "--dry-run" in sys.argv

    with GaiiaClient(timezone='America/Chicago') as g:
        print("Listing all active IP assignments...")
        assignments = list_all_assignments(g)
        print(f"\nFound {len(assignments)} active IP assignments")

        if dry_run:
            print("\n--dry-run mode: showing what would be expired --")
        else:
            print(f"\nExpiring via workflow {EXPIRE_WF_ID}...")

        succeeded = 0
        failed = 0
        for i, a in enumerate(assignments):
            if not dry_run:
                sys.stderr.write(f"\r  {i+1}/{len(assignments)}: {a['macAddress']} -> {a['ipAddress']}")
                sys.stderr.flush()
            if expire_one(g, a['macAddress'], a['ipAddress'], dry_run):
                succeeded += 1
            else:
                failed += 1
            if not dry_run:
                time.sleep(0.1)  # avoid rate limiting

        if not dry_run:
            sys.stderr.write("\n")

        print(f"\nDone: {succeeded} succeeded, {failed} failed")
        if dry_run:
            print("(nothing was changed — remove --dry-run to execute)")


if __name__ == '__main__':
    main()
