"""
reset_environment.py — DESTRUCTIVE wipe of all non-permanent data.

Clears:
  - Neo4j: drops ALL databases except system, neo4j (including gaisdata, cc.users.*, cc.institutes.*)
  - Supabase: deletes ALL data tables except gais_local_authorities and gais_schools
  - Supabase: deletes all auth users except kcar, then re-seeds kcar profile state

Safe invariants (never touched):
  - kcar auth account
  - gais_local_authorities and gais_schools Supabase tables
  - system / neo4j Neo4j system databases

Run from inside the ccapi container:
    python3 -c "from run.initialization.reset_environment import reset; reset()"
"""
import os
import time
import requests
from typing import List, Dict, Any

from modules.logger_tool import initialise_logger
import modules.database.tools.neo4j_driver_tools as dt

logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), "default", True)

KCAR_ID    = "d9e1d1a9-04c4-4611-bb05-57babf4a9a28"
KCAR_EMAIL = "kcar@kevlarai.com"

# Neo4j system databases — never drop these
NEO4J_SYSTEM_DBS = {"system", "neo4j"}

# Supabase tables to clear, in FK child-first order.
# gais_local_authorities and gais_schools are intentionally absent.
SUPABASE_TABLES_TO_CLEAR = [
    # ── Transcription (deepest children first) ───────────────────────────────
    "canvas_events",
    "keyword_events",
    "transcription_summaries",
    "transcription_segments",
    "keyword_watches",
    "transcription_sessions",
    # ── Lesson delivery chain ────────────────────────────────────────────────
    "lesson_deliveries",
    "lesson_collaborators",
    # ── Timetable materialization ────────────────────────────────────────────
    "taught_lessons",
    # ── Academic calendar (children → parents) ───────────────────────────────
    "academic_periods",
    "academic_days",
    "academic_weeks",
    "academic_term_breaks",
    "academic_terms",
    "academic_years",
    # ── Teacher timetables ───────────────────────────────────────────────────
    "teacher_timetable_slots",
    "teacher_timetables",
    "school_timetables",
    # ── Lesson plans ─────────────────────────────────────────────────────────
    "planned_lessons",
    # ── Whiteboard rooms ─────────────────────────────────────────────────────
    "whiteboard_rooms",
    # ── Classes & enrollment ─────────────────────────────────────────────────
    "enrollment_requests",
    "class_students",
    "class_teachers",
    "classes",
    # ── Files & brains ───────────────────────────────────────────────────────
    "document_artefacts",
    "brain_files",
    "cabinet_memberships",
    "files",
    "file_cabinets",
    "brains",
    # ── Invitations & memberships ────────────────────────────────────────────
    "invitations",
    "institute_memberships",
    "institute_membership_requests",
    # ── Institutes ───────────────────────────────────────────────────────────
    "institutes",
    # ── Profiles (non-kcar cleared separately via auth deletion cascade) ─────
    "admin_profiles",
]


def _sb_headers():
    url = os.environ["SUPABASE_URL"]
    key = os.environ["SERVICE_ROLE_KEY"]
    return url, {
        "apikey": key,
        "Authorization": f"Bearer {key}",
        "Content-Type": "application/json",
        "Prefer": "return=minimal",
    }


# ─── Neo4j helpers ────────────────────────────────────────────────────────────

def _neo4j_drop_all_non_system() -> Dict[str, List[str]]:
    """Drop every Neo4j DB except the system-reserved ones."""
    with dt.get_session(database="system") as s:
        all_dbs = [r["name"] for r in s.run("SHOW DATABASES YIELD name RETURN name")]

    to_drop = [db for db in all_dbs if db not in NEO4J_SYSTEM_DBS]
    dropped = []
    for db in to_drop:
        logger.info(f"  DROP DATABASE `{db}`")
        try:
            with dt.get_session(database="system") as s:
                s.run(f"DROP DATABASE `{db}` IF EXISTS")
            dropped.append(db)
        except Exception as e:
            logger.warning(f"  Could not drop `{db}`: {e}")
    return dropped


# ─── Supabase helpers ─────────────────────────────────────────────────────────

# Tables without an uid=1000(kcar) gid=1000(kcar) groups=1000(kcar),27(sudo),119(docker) column — map to the column to use as the delete filter.
TABLE_FILTER_COLUMN = {
    "brain_files": "brain_id",
}

def _sb_clear_table(url: str, headers: dict, table: str) -> int:
    """Delete all rows from a Supabase table. Returns HTTP status."""
    col = TABLE_FILTER_COLUMN.get(table, "id")
    r = requests.delete(
        f"{url}/rest/v1/{table}",
        headers=headers,
        params={col: "not.is.null"},
    )
    if r.status_code not in (200, 204):
        logger.warning(f"  Clear {table}: {r.status_code} {r.text[:120]}")
    return r.status_code


def _supabase_list_auth_users(url: str, headers: dict) -> List[Dict]:
    r = requests.get(f"{url}/auth/v1/admin/users", headers=headers, params={"per_page": 200})
    r.raise_for_status()
    return r.json().get("users", [])


def _supabase_delete_auth_user(url: str, headers: dict, uid: str):
    r = requests.delete(f"{url}/auth/v1/admin/users/{uid}", headers=headers)
    if r.status_code not in (200, 204):
        logger.warning(f"  Delete auth user {uid}: {r.status_code} {r.text[:80]}")


# ─── Main reset ───────────────────────────────────────────────────────────────

def reset() -> Dict[str, Any]:
    logger.info("=" * 60)
    logger.info("RESET ENVIRONMENT — full destructive wipe starting")
    logger.info("=" * 60)
    results: Dict[str, Any] = {}

    # ── 1. Neo4j: drop everything except system + neo4j ──────────────────────
    logger.info("\n[Neo4j] Dropping all non-system databases...")
    dropped = _neo4j_drop_all_non_system()
    logger.info(f"  Dropped {len(dropped)}: {dropped}")
    results["neo4j"] = {"dropped": dropped}

    # ── 2. Supabase: clear all data tables (GAIS preserved) ──────────────────
    logger.info("\n[Supabase] Clearing data tables (preserving gais_*)...")
    url, headers = _sb_headers()
    cleared, failed = [], []
    for table in SUPABASE_TABLES_TO_CLEAR:
        status = _sb_clear_table(url, headers, table)
        if status in (200, 204):
            cleared.append(table)
            logger.info(f"  ✓ {table}")
        else:
            failed.append(table)
    logger.info(f"  Cleared {len(cleared)} tables, {len(failed)} failed")

    # ── 3. Supabase: delete all auth users except kcar ────────────────────────
    logger.info("\n[Supabase] Deleting test auth users...")
    all_users = _supabase_list_auth_users(url, headers)
    deleted_emails = []
    for u in all_users:
        if u["email"] == KCAR_EMAIL:
            continue
        _supabase_delete_auth_user(url, headers, u["id"])
        deleted_emails.append(u["email"])
        time.sleep(0.05)
    logger.info(f"  Deleted {len(deleted_emails)} auth users")

    # Explicit cleanup in case cascade didn't fire
    requests.delete(f"{url}/rest/v1/profiles", headers=headers,
                    params={"id": f"neq.{KCAR_ID}"})

    # ── 4. Reset kcar profile to known-good platform_admin state ──────────────
    logger.info("\n[Supabase] Resetting kcar profile...")
    requests.patch(
        f"{url}/rest/v1/profiles",
        headers=headers,
        params={"id": f"eq.{KCAR_ID}"},
        json={"school_id": None},
    )
    logger.info("  kcar → school_id: null ✓")

    # Restore admin_profiles row (wiped with other tables above)
    requests.post(
        f"{url}/rest/v1/admin_profiles",
        headers={**headers, "Prefer": "resolution=merge-duplicates"},
        json={
            "id":             KCAR_ID,
            "email":          KCAR_EMAIL,
            "display_name":   "Kevin Carroll",
            "admin_role":     "super_admin",
            "is_super_admin": True,
        },
    )
    logger.info("  kcar → admin_profiles restored ✓")

    results["supabase"] = {
        "tables_cleared": cleared,
        "tables_failed": failed,
        "deleted_users": deleted_emails,
    }

    logger.info("\n" + "=" * 60)
    logger.info("RESET COMPLETE")
    logger.info("=" * 60)
    return results


if __name__ == "__main__":
    import json
    print(json.dumps(reset(), indent=2, default=str))
