import os
from fastapi import FastAPI
from fastapi.testclient import TestClient

from routers.me.bootstrap_router import router, auth_scheme
from modules.database.services.bootstrap_service import BootstrapService


USER_ID = "00000000-0000-0000-0000-000000000001"
INST_A = "10000000-0000-0000-0000-000000000001"
INST_B = "10000000-0000-0000-0000-000000000002"


class FakeResult:
    def __init__(self, data):
        self.data = data


class FakeQuery:
    def __init__(self, rows):
        self.rows = list(rows)
        self._single = False
        self._limit = None

    def select(self, *_args, **_kwargs):
        return self

    def eq(self, key, value):
        self.rows = [row for row in self.rows if row.get(key) == value]
        return self

    def in_(self, key, values):
        values = set(values)
        self.rows = [row for row in self.rows if row.get(key) in values]
        return self

    def order(self, *_args, **_kwargs):
        return self

    def limit(self, count):
        self._limit = count
        return self

    def single(self):
        self._single = True
        return self

    def execute(self):
        rows = self.rows[: self._limit] if self._limit is not None else self.rows
        if self._single:
            return FakeResult(rows[0] if rows else None)
        return FakeResult(rows)


class FakeSupabase:
    def __init__(self, tables):
        self.tables = tables

    def table(self, name):
        return FakeQuery(self.tables.get(name, []))


def profile(user_type="teacher", school_id=None):
    return {
        "id": USER_ID,
        "email": "teacher@example.test",
        "full_name": "Example Teacher",
        "display_name": None,
        "user_type": user_type,
        "school_id": school_id,
    }


def institute(id_=INST_A, name="Example School", status="active"):
    return {
        "id": id_,
        "name": name,
        "urn": "123456",
        "website": "https://school.example",
        "address": {"town": "Testville"},
        "metadata": {"internal_note": "should remain institute metadata only"},
        "status": status,
        "neo4j_uuid_string": "neo-a" if id_ == INST_A else "neo-b",
    }


def membership(institute_id=INST_A, role="teacher"):
    return {"profile_id": USER_ID, "institute_id": institute_id, "role": role}


def service(tables, graph_probe=None):
    return BootstrapService(FakeSupabase(tables), graph_probe=graph_probe or (lambda **_: {"available": True, "projection_state": "ready"}))


def build(credentials=None, tables=None, graph_probe=None):
    credentials = credentials or {"sub": USER_ID, "email": "teacher@example.test"}
    tables = tables or {"profiles": [profile()]}
    return service(tables, graph_probe).build(credentials)


def test_supabase_client_for_user_uses_access_token_authorization(monkeypatch):
    from modules.database.supabase.utils import client as client_module

    captured = {}

    class FakeOptions:
        def __init__(self, **kwargs):
            captured["options_kwargs"] = kwargs

    def fake_create_client(url, key, options=None):
        captured["url"] = url
        captured["key"] = key
        captured["options"] = options
        return {"ok": True}

    monkeypatch.setenv("SUPABASE_URL", "http://supabase.test")
    monkeypatch.setenv("ANON_KEY", "anon-key")
    monkeypatch.setattr(client_module, "SyncClientOptions", FakeOptions)
    monkeypatch.setattr(client_module, "create_client", fake_create_client)

    anon = client_module.SupabaseAnonClient.for_user("user-token")

    assert anon.access_token == "user-token"
    assert captured["url"] == "http://supabase.test"
    # apikey is supplied via the `key` positional arg (supabase-py sets the apikey header from it).
    # options.headers must carry ONLY the per-user Authorization override — adding apikey here too
    # produces a duplicate apikey header that Kong rejects ("Duplicate API key found").
    assert captured["key"] == "anon-key"
    assert captured["options_kwargs"]["headers"] == {"Authorization": "Bearer user-token"}


def test_no_school_bootstrap_requires_school_membership_but_allows_canvas():
    payload = build(tables={"profiles": [profile()]})

    assert payload["school_status"] == "no_school"
    assert payload["active_institute"]["source"] == "none"
    assert payload["memberships"] == []
    assert payload["permissions"]["can_create_school"] is True
    assert payload["permissions"]["can_use_canvas"] is True
    assert payload["onboarding"]["next_step"] == "create_or_join_school"
    assert "school_membership" in payload["onboarding"]["required"]


def test_single_membership_becomes_active_and_uses_supabase_calendar_timetable_status():
    payload = build(tables={
        "profiles": [profile()],
        "institute_memberships": [membership()],
        "institutes": [institute()],
        "academic_years": [{"id": "ay-1", "institute_id": INST_A, "is_current": True}],
        "academic_terms": [{"id": "term-1", "institute_id": INST_A}],
        "school_timetables": [{"id": "school-tt-1", "institute_id": INST_A}],
        "teacher_timetables": [{"id": "teacher-tt-1", "institute_id": INST_A, "teacher_profile_id": USER_ID}],
        "teacher_timetable_slots": [
            {"id": "slot-1", "teacher_timetable_id": "teacher-tt-1"},
            {"id": "slot-2", "teacher_timetable_id": "teacher-tt-1"},
        ],
    })

    assert payload["school_status"] == "member"
    assert payload["active_institute"] == {"id": INST_A, "source": "single_membership", "membership_role": "teacher"}
    assert payload["calendar_status"] == {
        "available": True,
        "academic_year_count": 1,
        "term_count": 1,
        "current_academic_year_id": "ay-1",
        "needs_setup": False,
    }
    assert payload["timetable_status"] == {
        "available": True,
        "teacher_timetable_id": "teacher-tt-1",
        "slot_count": 2,
        "needs_setup": False,
    }
    assert payload["onboarding"]["next_step"] == "ready"


def test_multiple_memberships_require_selection_when_no_profile_school_id_matches():
    payload = build(tables={
        "profiles": [profile()],
        "institute_memberships": [membership(INST_A, "teacher"), membership(INST_B, "department_head")],
        "institutes": [institute(INST_A, "Alpha"), institute(INST_B, "Beta")],
    })

    assert payload["school_status"] == "multi_school_needs_selection"
    assert payload["active_institute"] == {"id": None, "source": "none", "membership_role": None}
    assert payload["onboarding"]["next_step"] == "select_school"
    assert "active_school_selection" in payload["onboarding"]["required"]


def test_school_admin_permissions_and_onboarding_invite_staff_after_calendar_timetable_ready():
    payload = build(tables={
        "profiles": [profile(user_type="school_admin", school_id=INST_A)],
        "institute_memberships": [membership(INST_A, "school_admin")],
        "institutes": [institute()],
        "academic_years": [{"id": "ay-1", "institute_id": INST_A, "is_current": True}],
        "academic_terms": [{"id": "term-1", "institute_id": INST_A}],
        "teacher_timetables": [{"id": "teacher-tt-1", "institute_id": INST_A, "teacher_profile_id": USER_ID}],
        "teacher_timetable_slots": [{"id": "slot-1", "teacher_timetable_id": "teacher-tt-1"}],
    })

    assert payload["school_status"] == "school_admin"
    perms = payload["permissions"]
    assert perms["can_manage_school"] is True
    assert perms["can_manage_calendar"] is True
    assert perms["can_manage_timetable"] is True
    assert perms["can_invite_staff"] is True
    assert perms["can_view_student_data"] is True
    assert payload["onboarding"]["next_step"] == "invite_staff"


def test_platform_admin_uses_admin_profiles_without_relying_on_profile_super_admin_type():
    payload = build(credentials={"sub": USER_ID, "email": "admin@example.test"}, tables={
        "profiles": [profile(user_type="teacher")],
        "admin_profiles": [{"id": USER_ID, "admin_role": "owner", "is_super_admin": True}],
    })

    assert payload["profile"]["user_type"] == "platform_admin"
    assert payload["school_status"] == "platform_admin"
    assert payload["permissions"]["platform_admin"] is True
    assert payload["permissions"]["platform_super_admin"] is True
    assert payload["permissions"]["can_create_school"] is True


def test_platform_admin_membership_role_does_not_reduce_platform_permissions():
    payload = build(credentials={"sub": USER_ID, "email": "admin@example.test"}, tables={
        "profiles": [profile(user_type="student")],
        "admin_profiles": [{"id": USER_ID, "admin_role": "owner", "is_super_admin": True}],
        "institute_memberships": [membership(INST_A, "student")],
        "institutes": [institute()],
    })

    assert payload["school_status"] == "platform_admin"
    assert payload["permissions"]["platform_admin"] is True
    assert payload["permissions"]["can_manage_school"] is True
    assert payload["permissions"]["can_manage_calendar"] is True
    assert payload["permissions"]["can_view_student_data"] is True


def test_neo4j_probe_failure_does_not_block_supabase_bootstrap_state():
    def failing_probe(**_kwargs):
        raise RuntimeError("connection refused with host details that must not leak")

    payload = build(tables={
        "profiles": [profile()],
        "institute_memberships": [membership()],
        "institutes": [institute()],
    }, graph_probe=failing_probe)

    assert payload["school_status"] == "member"
    assert payload["active_institute"]["id"] == INST_A
    assert payload["graph_status"]["available"] is False
    assert payload["graph_status"]["projection_state"] == "error"
    assert payload["graph_status"]["needs_rebuild"] is True
    assert payload["graph_status"]["error_code"] == "neo4j_unavailable"
    assert "connection refused" not in str(payload)


def test_route_is_authenticated_and_does_not_return_raw_auth_metadata(monkeypatch):
    app = FastAPI()
    app.include_router(router, prefix="/me")
    app.dependency_overrides[auth_scheme] = lambda: {"sub": USER_ID, "email": "teacher@example.test", "app_metadata": {"secret": "nope"}}

    monkeypatch.setattr(
        "routers.me.bootstrap_router.build_bootstrap_response",
        lambda credentials: {
            "profile": {"id": USER_ID, "email": credentials["email"], "display_name": "Example", "user_type": "teacher", "school_id": None},
            "memberships": [],
            "active_institute": {"id": None, "source": "none", "membership_role": None},
            "permissions": {"platform_admin": False},
            "school_status": "no_school",
            "onboarding": {"next_step": "create_or_join_school", "required": [], "optional": [], "message": "Create or join a school."},
            "calendar_status": {"available": False, "academic_year_count": 0, "term_count": 0, "current_academic_year_id": None, "needs_setup": True},
            "timetable_status": {"available": False, "teacher_timetable_id": None, "slot_count": 0, "needs_setup": True},
            "graph_status": {"available": False, "user_db": None, "institute_db": None, "projection_state": "unknown", "needs_rebuild": True, "last_checked_at": "2026-05-28T00:00:00Z", "error_code": None},
        },
    )

    response = TestClient(app).get("/me/bootstrap")
    assert response.status_code == 200
    assert "app_metadata" not in response.text
    assert "secret" not in response.text
