-- ============================================================
-- Classroom Copilot — Application Schema
-- Migration 002: All application tables (non-GAIS)
-- Run after: 001_gais_seed.sql
-- ============================================================

-- Extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

-- ============================================================
-- 1. Core user & school tables
-- ============================================================

-- 1.1 Profiles — mirrors auth.users, extended user data
CREATE TABLE IF NOT EXISTS profiles (
    id           UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
    email        TEXT NOT NULL,
    user_type    TEXT NOT NULL CHECK (user_type IN ('teacher','student','admin')),
    username     TEXT NOT NULL UNIQUE,
    full_name    TEXT,
    display_name TEXT,
    school_id    UUID,                       -- FK to institutes added below (circular avoidance)
    metadata     JSONB NOT NULL DEFAULT '{}',
    neo4j_sync_status TEXT DEFAULT 'pending', -- tracks Neo4j knowledge-graph sync
    neo4j_synced_at   TIMESTAMPTZ,
    last_login   TIMESTAMPTZ,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 1.2 Admin profiles — separate table for system-level admins
CREATE TABLE IF NOT EXISTS admin_profiles (
    id           UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
    email        TEXT NOT NULL,
    display_name TEXT,
    admin_role   TEXT NOT NULL DEFAULT 'admin',
    is_super_admin BOOLEAN NOT NULL DEFAULT false,
    metadata     JSONB NOT NULL DEFAULT '{}',
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 1.3 Institutes (schools)
CREATE TABLE IF NOT EXISTS institutes (
    id                        UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    name                      TEXT NOT NULL,
    urn                       TEXT UNIQUE,
    status                    TEXT NOT NULL DEFAULT 'active',
    address                   JSONB NOT NULL DEFAULT '{}',
    website                   TEXT,
    metadata                  JSONB NOT NULL DEFAULT '{}',
    geo_coordinates           JSONB NOT NULL DEFAULT '{}',
    neo4j_uuid_string         TEXT,
    neo4j_public_sync_status  TEXT DEFAULT 'pending',
    neo4j_public_sync_at      TIMESTAMPTZ,
    neo4j_private_sync_status TEXT DEFAULT 'not_started',
    neo4j_private_sync_at     TIMESTAMPTZ,
    created_at                TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at                TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Deferred FK: profiles.school_id → institutes
ALTER TABLE profiles
    DROP CONSTRAINT IF EXISTS profiles_school_id_fkey;
ALTER TABLE profiles
    ADD CONSTRAINT profiles_school_id_fkey
    FOREIGN KEY (school_id) REFERENCES institutes(id) ON DELETE SET NULL;

-- 1.4 Institute memberships
CREATE TABLE IF NOT EXISTS institute_memberships (
    id           UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    profile_id   UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    institute_id UUID NOT NULL REFERENCES institutes(id) ON DELETE CASCADE,
    role         TEXT NOT NULL CHECK (role IN ('school_admin','teacher','student')),
    tldraw_preferences JSONB NOT NULL DEFAULT '{}',
    metadata     JSONB NOT NULL DEFAULT '{}',
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (profile_id, institute_id)
);

-- 1.5 Institute membership requests (teacher invite / student join flow)
CREATE TABLE IF NOT EXISTS institute_membership_requests (
    id             UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    profile_id     UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    institute_id   UUID NOT NULL REFERENCES institutes(id) ON DELETE CASCADE,
    requested_role TEXT NOT NULL,
    status         TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','rejected')),
    metadata       JSONB NOT NULL DEFAULT '{}',
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now()
);


-- ============================================================
-- 2. TLDraw whiteboard rooms
-- ============================================================

CREATE TABLE IF NOT EXISTS whiteboard_rooms (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id      UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    institute_id UUID REFERENCES institutes(id) ON DELETE SET NULL,
    name         TEXT NOT NULL DEFAULT 'My Workspace',
    context_type TEXT NOT NULL DEFAULT 'profile',
    context_id   TEXT,
    is_default   BOOLEAN NOT NULL DEFAULT false,
    storage_path TEXT,
    neo4j_node_id TEXT,
    neo4j_db_name TEXT,
    node_type     TEXT,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);


-- ============================================================
-- 3. File cabinet system
-- ============================================================

-- 3.1 Cabinets — top-level containers owned by a user
CREATE TABLE IF NOT EXISTS file_cabinets (
    id         UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id    UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    name       TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 3.2 Files — records for files stored in Supabase Storage
CREATE TABLE IF NOT EXISTS files (
    id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    cabinet_id          UUID NOT NULL REFERENCES file_cabinets(id) ON DELETE CASCADE,
    uploaded_by         UUID REFERENCES profiles(id) ON DELETE SET NULL,
    name                TEXT NOT NULL,
    path                TEXT NOT NULL,
    bucket              TEXT NOT NULL DEFAULT 'cc.users',
    mime_type           TEXT,
    size_bytes          BIGINT,
    size                TEXT,
    category            TEXT,
    source              TEXT DEFAULT 'uploader-web',
    is_directory        BOOLEAN NOT NULL DEFAULT false,
    parent_directory_id UUID REFERENCES files(id) ON DELETE SET NULL,
    relative_path       TEXT,
    directory_manifest  JSONB,
    upload_session_id   UUID,
    processing_status   TEXT NOT NULL DEFAULT 'uploaded',
    metadata            JSONB NOT NULL DEFAULT '{}',
    created_at          TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 3.3 Cabinet memberships — share a cabinet with other users
CREATE TABLE IF NOT EXISTS cabinet_memberships (
    id         UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    cabinet_id UUID NOT NULL REFERENCES file_cabinets(id) ON DELETE CASCADE,
    profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    role       TEXT NOT NULL DEFAULT 'viewer' CHECK (role IN ('viewer','editor','owner')),
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (cabinet_id, profile_id)
);

-- 3.4 Document artefacts — processed outputs from files (Docling, Tika, etc.)
CREATE TABLE IF NOT EXISTS document_artefacts (
    id            UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    file_id       UUID NOT NULL REFERENCES files(id) ON DELETE CASCADE,
    type          TEXT NOT NULL,
    rel_path      TEXT NOT NULL,
    page_number   INTEGER NOT NULL DEFAULT 0,
    chunk_index   INTEGER,
    size_tag      TEXT,
    language      TEXT,
    extra         JSONB,
    status        TEXT NOT NULL DEFAULT 'completed',
    started_at    TIMESTAMPTZ DEFAULT now(),
    completed_at  TIMESTAMPTZ,
    error_message TEXT,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);


-- ============================================================
-- 4. Knowledge banks (brains) — Phase G: RAG over file collections
-- ============================================================

CREATE TABLE IF NOT EXISTS brains (
    id         UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id    UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    name       TEXT NOT NULL,
    purpose    TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS brain_files (
    brain_id UUID NOT NULL REFERENCES brains(id) ON DELETE CASCADE,
    file_id  UUID NOT NULL REFERENCES files(id) ON DELETE CASCADE,
    PRIMARY KEY (brain_id, file_id)
);


-- ============================================================
-- 5. Class system
-- ============================================================

-- 5.1 Classes
CREATE TABLE IF NOT EXISTS classes (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    institute_id UUID NOT NULL REFERENCES institutes(id) ON DELETE CASCADE,
    name         VARCHAR NOT NULL,
    class_code   TEXT,               -- MIS identifier e.g. '9YO/Bi', '10Da'
    subject      VARCHAR,
    key_stage    TEXT,               -- '3', '4', '5'
    year_group   VARCHAR,
    academic_year VARCHAR,
    description  TEXT,
    type         VARCHAR NOT NULL DEFAULT 'standard',
    is_active    BOOLEAN NOT NULL DEFAULT true,
    created_by   UUID NOT NULL REFERENCES profiles(id),
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 5.2 Class teachers
CREATE TABLE IF NOT EXISTS class_teachers (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    class_id    UUID NOT NULL REFERENCES classes(id) ON DELETE CASCADE,
    teacher_id  UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    is_primary  BOOLEAN NOT NULL DEFAULT false,
    can_edit    BOOLEAN NOT NULL DEFAULT true,
    assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    assigned_by UUID REFERENCES profiles(id),
    UNIQUE (class_id, teacher_id)
);

-- 5.3 Class students
CREATE TABLE IF NOT EXISTS class_students (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    class_id    UUID NOT NULL REFERENCES classes(id) ON DELETE CASCADE,
    student_id  UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    status      VARCHAR NOT NULL DEFAULT 'active' CHECK (status IN ('active','inactive','pending')),
    enrolled_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    enrolled_by UUID REFERENCES profiles(id),
    UNIQUE (class_id, student_id)
);

-- 5.4 Enrollment requests — student self-enrollment flow (Phase D)
CREATE TABLE IF NOT EXISTS enrollment_requests (
    id               UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    class_id         UUID NOT NULL REFERENCES classes(id) ON DELETE CASCADE,
    student_id       UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    status           VARCHAR NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','rejected')),
    request_message  TEXT,
    requested_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    responded_at     TIMESTAMPTZ,
    responded_by     UUID REFERENCES profiles(id),
    response_message TEXT
);


-- ============================================================
-- 6. Curriculum reference (flat Supabase lookup — full graph in Neo4j Phase G)
-- ============================================================

-- 6.0 Curriculum topics — importable from curriculum.xlsx, referenced by planned_lessons
CREATE TABLE IF NOT EXISTS curriculum_topics (
    id           TEXT PRIMARY KEY,      -- e.g. '7B1', '10P10' — matches xlsx TopicID
    title        TEXT NOT NULL,
    subject      TEXT,
    key_stage    TEXT,                  -- '3', '4', '5'
    year_group   TEXT,
    topic_type   TEXT,                  -- 'Standard', 'Assessment', etc.
    total_lessons INTEGER,
    department   TEXT,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- ============================================================
-- 7. Lesson planning — Phase C
-- ============================================================

-- 7.1 Planned lessons — teacher-authored lesson plans
CREATE TABLE IF NOT EXISTS planned_lessons (
    id                         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    created_by                 UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    institute_id               UUID NOT NULL REFERENCES institutes(id),
    class_id                   UUID REFERENCES classes(id) ON DELETE SET NULL,
    whiteboard_room_id         UUID REFERENCES whiteboard_rooms(id) ON DELETE SET NULL,
    topic_code                 TEXT REFERENCES curriculum_topics(id) ON DELETE SET NULL,
    timetable_period_id        TEXT,           -- Neo4j period node reference (e.g. 'AMon1')
    title                      TEXT NOT NULL,
    subject                    TEXT,
    year_group                 TEXT,
    estimated_duration_minutes INTEGER,
    objectives                 JSONB NOT NULL DEFAULT '[]',
    activities                 JSONB NOT NULL DEFAULT '[]',
    status                     TEXT NOT NULL DEFAULT 'draft'
                                   CHECK (status IN ('draft','ready','archived')),
    tags                       TEXT[] NOT NULL DEFAULT '{}',
    created_at                 TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at                 TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 6.2 Lesson collaborators — co-planning: additional teachers on a lesson
CREATE TABLE IF NOT EXISTS lesson_collaborators (
    id                UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    planned_lesson_id UUID NOT NULL REFERENCES planned_lessons(id) ON DELETE CASCADE,
    profile_id        UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    can_edit          BOOLEAN NOT NULL DEFAULT true,
    added_at          TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (planned_lesson_id, profile_id)
);

-- 6.3 Lesson deliveries — records of when a planned lesson is actually taught
CREATE TABLE IF NOT EXISTS lesson_deliveries (
    id                       UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    planned_lesson_id        UUID REFERENCES planned_lessons(id) ON DELETE SET NULL,
    delivered_by             UUID NOT NULL REFERENCES profiles(id),
    class_id                 UUID REFERENCES classes(id) ON DELETE SET NULL,
    institute_id             UUID NOT NULL REFERENCES institutes(id),
    whiteboard_room_id       UUID REFERENCES whiteboard_rooms(id) ON DELETE SET NULL,
    transcription_session_id UUID,    -- FK to transcription_sessions added after CIS tables
    started_at               TIMESTAMPTZ NOT NULL DEFAULT now(),
    ended_at                 TIMESTAMPTZ,
    notes                    TEXT,
    created_at               TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at               TIMESTAMPTZ NOT NULL DEFAULT now()
);


-- ============================================================
-- 8. Exam board reference (Phase F)
-- ============================================================

CREATE TABLE IF NOT EXISTS eb_specifications (
    id             UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    spec_code      TEXT UNIQUE,
    exam_board_code TEXT,
    award_code     TEXT,
    subject_code   TEXT,
    first_teach    TEXT,
    spec_ver       TEXT,
    storage_loc    TEXT,
    doc_type       TEXT,
    doc_details    JSONB NOT NULL DEFAULT '{}',
    docling_docs   JSONB NOT NULL DEFAULT '{}',
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS eb_exams (
    id          UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    exam_code   TEXT,
    spec_code   TEXT REFERENCES eb_specifications(spec_code),
    paper_code  TEXT,
    tier        TEXT,
    session     TEXT,
    type_code   TEXT,
    storage_loc TEXT,
    doc_type    TEXT,
    doc_details JSONB NOT NULL DEFAULT '{}',
    docling_docs JSONB NOT NULL DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);


-- ============================================================
-- 9. CIS: Transcription & Canvas event system
-- ============================================================

-- 8.1 Transcription sessions
CREATE TABLE IF NOT EXISTS transcription_sessions (
    id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id               UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    title                 TEXT,
    canvas_type           TEXT DEFAULT 'tldraw',
    started_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
    ended_at              TIMESTAMPTZ,
    duration_seconds      INTEGER,
    timetable_period_id   TEXT,
    timetable_event_type  TEXT,
    timetable_event_label TEXT,
    auto_tagged           BOOLEAN NOT NULL DEFAULT false,
    llm_provider          TEXT,
    llm_model             TEXT,
    word_count            INTEGER NOT NULL DEFAULT 0,
    segment_count         INTEGER NOT NULL DEFAULT 0,
    metadata              JSONB NOT NULL DEFAULT '{}',
    deleted_at            TIMESTAMPTZ,
    created_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at            TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 8.2 Transcription segments
CREATE TABLE IF NOT EXISTS transcription_segments (
    id             UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id     UUID NOT NULL REFERENCES transcription_sessions(id) ON DELETE CASCADE,
    sequence_index INTEGER NOT NULL,
    text           TEXT NOT NULL,
    start_seconds  REAL NOT NULL DEFAULT 0,
    end_seconds    REAL NOT NULL DEFAULT 0,
    is_final       BOOLEAN NOT NULL DEFAULT true,
    speaker_label  TEXT,
    keyword_matches TEXT[] NOT NULL DEFAULT '{}',
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 8.3 Canvas events (TLDraw interactions during a session)
CREATE TABLE IF NOT EXISTS canvas_events (
    id                     UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id             UUID REFERENCES transcription_sessions(id) ON DELETE CASCADE,
    user_id                UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    timestamp              TIMESTAMPTZ NOT NULL DEFAULT now(),
    session_elapsed_seconds REAL,
    event_type             TEXT NOT NULL,
    event_payload          JSONB NOT NULL DEFAULT '{}',
    canvas_snapshot_url    TEXT,
    tldraw_page_id         TEXT,
    tldraw_shape_ids       TEXT[] NOT NULL DEFAULT '{}',
    created_at             TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 8.4 AI-generated summaries
CREATE TABLE IF NOT EXISTS transcription_summaries (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id          UUID NOT NULL REFERENCES transcription_sessions(id) ON DELETE CASCADE,
    user_id             UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    summary_type        TEXT NOT NULL,
    content             TEXT NOT NULL,
    prompt_used         TEXT,
    llm_provider        TEXT,
    llm_model           TEXT,
    input_tokens        INTEGER,
    output_tokens       INTEGER,
    segment_range_start INTEGER,
    segment_range_end   INTEGER,
    canvas_snapshot_urls TEXT[] NOT NULL DEFAULT '{}',
    created_at          TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 8.5 Keyword watch list
CREATE TABLE IF NOT EXISTS keyword_watches (
    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id    UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    keyword    TEXT NOT NULL,
    match_type TEXT NOT NULL DEFAULT 'contains',
    action     TEXT NOT NULL DEFAULT 'notify',
    is_active  BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (user_id, keyword)
);

-- 8.6 Keyword events
CREATE TABLE IF NOT EXISTS keyword_events (
    id                     UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id             UUID NOT NULL REFERENCES transcription_sessions(id) ON DELETE CASCADE,
    segment_id             UUID REFERENCES transcription_segments(id) ON DELETE SET NULL,
    keyword_watch_id       UUID REFERENCES keyword_watches(id) ON DELETE SET NULL,
    keyword_text           TEXT NOT NULL,
    matched_in_text        TEXT NOT NULL,
    session_elapsed_seconds REAL,
    created_at             TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Deferred FK: lesson_deliveries.transcription_session_id
ALTER TABLE lesson_deliveries
    DROP CONSTRAINT IF EXISTS lesson_deliveries_transcription_session_id_fkey;
ALTER TABLE lesson_deliveries
    ADD CONSTRAINT lesson_deliveries_transcription_session_id_fkey
    FOREIGN KEY (transcription_session_id)
    REFERENCES transcription_sessions(id) ON DELETE SET NULL;


-- ============================================================
-- 9. Updated_at trigger
-- ============================================================

CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = now();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DO $$ DECLARE
    t TEXT;
BEGIN
    FOREACH t IN ARRAY ARRAY[
        'profiles', 'admin_profiles', 'institutes',
        'institute_memberships', 'institute_membership_requests',
        'whiteboard_rooms', 'cabinet_memberships',
        'classes', 'planned_lessons', 'lesson_deliveries',
        'transcription_sessions', 'keyword_watches',
        'eb_specifications', 'eb_exams'
    ] LOOP
        EXECUTE format(
            'DROP TRIGGER IF EXISTS trg_updated_at ON %I;
             CREATE TRIGGER trg_updated_at
             BEFORE UPDATE ON %I
             FOR EACH ROW EXECUTE FUNCTION set_updated_at();',
            t, t
        );
    END LOOP;
END $$;


-- ============================================================
-- 10. Indexes
-- ============================================================

-- Profiles
CREATE INDEX IF NOT EXISTS idx_profiles_school_id      ON profiles(school_id);
CREATE INDEX IF NOT EXISTS idx_profiles_email          ON profiles(email);
CREATE INDEX IF NOT EXISTS idx_profiles_username       ON profiles(username);

-- Institutes
CREATE INDEX IF NOT EXISTS idx_institutes_urn          ON institutes(urn);

-- Institute memberships
CREATE INDEX IF NOT EXISTS idx_im_profile              ON institute_memberships(profile_id);
CREATE INDEX IF NOT EXISTS idx_im_institute            ON institute_memberships(institute_id);

-- Whiteboard rooms
CREATE INDEX IF NOT EXISTS idx_wr_user                 ON whiteboard_rooms(user_id);
CREATE INDEX IF NOT EXISTS idx_wr_context              ON whiteboard_rooms(context_type, context_id);

-- Files
CREATE INDEX IF NOT EXISTS idx_files_cabinet           ON files(cabinet_id);
CREATE INDEX IF NOT EXISTS idx_files_uploaded_by       ON files(uploaded_by);
CREATE INDEX IF NOT EXISTS idx_files_status            ON files(processing_status);
CREATE INDEX IF NOT EXISTS idx_files_parent            ON files(parent_directory_id);

-- Document artefacts
CREATE INDEX IF NOT EXISTS idx_artefacts_file          ON document_artefacts(file_id);
CREATE INDEX IF NOT EXISTS idx_artefacts_type          ON document_artefacts(type);

-- Cabinet memberships
CREATE INDEX IF NOT EXISTS idx_cabinet_mb_profile      ON cabinet_memberships(profile_id);

-- Brains
CREATE INDEX IF NOT EXISTS idx_brains_user             ON brains(user_id);

-- Curriculum topics
CREATE INDEX IF NOT EXISTS idx_ct_subject              ON curriculum_topics(subject);
CREATE INDEX IF NOT EXISTS idx_ct_key_stage            ON curriculum_topics(key_stage);
CREATE INDEX IF NOT EXISTS idx_ct_year_group           ON curriculum_topics(year_group);

-- Classes
CREATE INDEX IF NOT EXISTS idx_classes_institute       ON classes(institute_id);
CREATE INDEX IF NOT EXISTS idx_classes_class_code      ON classes(class_code);
CREATE INDEX IF NOT EXISTS idx_classes_created_by      ON classes(created_by);
CREATE INDEX IF NOT EXISTS idx_class_teachers_class    ON class_teachers(class_id);
CREATE INDEX IF NOT EXISTS idx_class_students_class    ON class_students(class_id);

-- Planned lessons
CREATE INDEX IF NOT EXISTS idx_pl_created_by           ON planned_lessons(created_by);
CREATE INDEX IF NOT EXISTS idx_pl_institute            ON planned_lessons(institute_id);
CREATE INDEX IF NOT EXISTS idx_pl_class                ON planned_lessons(class_id);
CREATE INDEX IF NOT EXISTS idx_pl_status               ON planned_lessons(status);
CREATE INDEX IF NOT EXISTS idx_pl_topic_code           ON planned_lessons(topic_code);
CREATE INDEX IF NOT EXISTS idx_pl_timetable_period     ON planned_lessons(timetable_period_id);

-- Lesson deliveries
CREATE INDEX IF NOT EXISTS idx_ld_delivered_by         ON lesson_deliveries(delivered_by);
CREATE INDEX IF NOT EXISTS idx_ld_planned_lesson       ON lesson_deliveries(planned_lesson_id);
CREATE INDEX IF NOT EXISTS idx_ld_started_at           ON lesson_deliveries(started_at DESC);

-- CIS
CREATE INDEX IF NOT EXISTS idx_ts_user                 ON transcription_sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_ts_started              ON transcription_sessions(started_at DESC);
CREATE INDEX IF NOT EXISTS idx_ts_deleted              ON transcription_sessions(deleted_at) WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_seg_session             ON transcription_segments(session_id, sequence_index);
CREATE INDEX IF NOT EXISTS idx_ce_session              ON canvas_events(session_id, timestamp);
CREATE INDEX IF NOT EXISTS idx_ce_user                 ON canvas_events(user_id, timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_kw_user                 ON keyword_watches(user_id) WHERE is_active = true;
CREATE INDEX IF NOT EXISTS idx_ke_session              ON keyword_events(session_id);


-- ============================================================
-- 11. Row Level Security
-- ============================================================

-- Enable RLS
ALTER TABLE profiles                    ENABLE ROW LEVEL SECURITY;
ALTER TABLE admin_profiles              ENABLE ROW LEVEL SECURITY;
ALTER TABLE institutes                  ENABLE ROW LEVEL SECURITY;
ALTER TABLE institute_memberships       ENABLE ROW LEVEL SECURITY;
ALTER TABLE institute_membership_requests ENABLE ROW LEVEL SECURITY;
ALTER TABLE whiteboard_rooms            ENABLE ROW LEVEL SECURITY;
ALTER TABLE file_cabinets               ENABLE ROW LEVEL SECURITY;
ALTER TABLE files                       ENABLE ROW LEVEL SECURITY;
ALTER TABLE cabinet_memberships         ENABLE ROW LEVEL SECURITY;
ALTER TABLE document_artefacts          ENABLE ROW LEVEL SECURITY;
ALTER TABLE brains                      ENABLE ROW LEVEL SECURITY;
ALTER TABLE brain_files                 ENABLE ROW LEVEL SECURITY;
ALTER TABLE classes                     ENABLE ROW LEVEL SECURITY;
ALTER TABLE class_teachers              ENABLE ROW LEVEL SECURITY;
ALTER TABLE class_students              ENABLE ROW LEVEL SECURITY;
ALTER TABLE enrollment_requests         ENABLE ROW LEVEL SECURITY;
ALTER TABLE planned_lessons             ENABLE ROW LEVEL SECURITY;
ALTER TABLE lesson_collaborators        ENABLE ROW LEVEL SECURITY;
ALTER TABLE lesson_deliveries           ENABLE ROW LEVEL SECURITY;
ALTER TABLE curriculum_topics           ENABLE ROW LEVEL SECURITY;
ALTER TABLE eb_specifications           ENABLE ROW LEVEL SECURITY;
ALTER TABLE eb_exams                    ENABLE ROW LEVEL SECURITY;
ALTER TABLE transcription_sessions      ENABLE ROW LEVEL SECURITY;
ALTER TABLE transcription_segments      ENABLE ROW LEVEL SECURITY;
ALTER TABLE canvas_events               ENABLE ROW LEVEL SECURITY;
ALTER TABLE transcription_summaries     ENABLE ROW LEVEL SECURITY;
ALTER TABLE keyword_watches             ENABLE ROW LEVEL SECURITY;
ALTER TABLE keyword_events              ENABLE ROW LEVEL SECURITY;

-- Profiles: own row + service role full access
DROP POLICY IF EXISTS "profiles_own"           ON profiles;
DROP POLICY IF EXISTS "profiles_service_role"  ON profiles;
CREATE POLICY "profiles_own"          ON profiles FOR ALL USING (id = auth.uid());
CREATE POLICY "profiles_service_role" ON profiles FOR ALL USING (auth.role() = 'service_role');

-- Admin profiles: super admins only
DROP POLICY IF EXISTS "admin_profiles_service_role" ON admin_profiles;
CREATE POLICY "admin_profiles_service_role" ON admin_profiles FOR ALL USING (auth.role() = 'service_role');

-- Institutes: members can read, school_admin can write, service role full access
DROP POLICY IF EXISTS "institutes_member_read"    ON institutes;
DROP POLICY IF EXISTS "institutes_service_role"   ON institutes;
CREATE POLICY "institutes_member_read" ON institutes FOR SELECT
    USING (id IN (SELECT institute_id FROM institute_memberships WHERE profile_id = auth.uid()));
CREATE POLICY "institutes_service_role" ON institutes FOR ALL USING (auth.role() = 'service_role');

-- Institute memberships
DROP POLICY IF EXISTS "im_own"           ON institute_memberships;
DROP POLICY IF EXISTS "im_service_role"  ON institute_memberships;
CREATE POLICY "im_own"          ON institute_memberships FOR ALL USING (profile_id = auth.uid());
CREATE POLICY "im_service_role" ON institute_memberships FOR ALL USING (auth.role() = 'service_role');

-- Institute membership requests
DROP POLICY IF EXISTS "imr_own"           ON institute_membership_requests;
DROP POLICY IF EXISTS "imr_service_role"  ON institute_membership_requests;
CREATE POLICY "imr_own"          ON institute_membership_requests FOR ALL USING (profile_id = auth.uid());
CREATE POLICY "imr_service_role" ON institute_membership_requests FOR ALL USING (auth.role() = 'service_role');

-- Whiteboard rooms: own rooms
DROP POLICY IF EXISTS "wr_own"           ON whiteboard_rooms;
DROP POLICY IF EXISTS "wr_service_role"  ON whiteboard_rooms;
CREATE POLICY "wr_own"          ON whiteboard_rooms FOR ALL USING (user_id = auth.uid());
CREATE POLICY "wr_service_role" ON whiteboard_rooms FOR ALL USING (auth.role() = 'service_role');

-- File cabinets: own cabinets
DROP POLICY IF EXISTS "fc_own"           ON file_cabinets;
DROP POLICY IF EXISTS "fc_service_role"  ON file_cabinets;
CREATE POLICY "fc_own"          ON file_cabinets FOR ALL USING (user_id = auth.uid());
CREATE POLICY "fc_service_role" ON file_cabinets FOR ALL USING (auth.role() = 'service_role');

-- Files: own via cabinet
DROP POLICY IF EXISTS "files_own"           ON files;
DROP POLICY IF EXISTS "files_service_role"  ON files;
CREATE POLICY "files_own" ON files FOR ALL
    USING (cabinet_id IN (SELECT id FROM file_cabinets WHERE user_id = auth.uid()));
CREATE POLICY "files_service_role" ON files FOR ALL USING (auth.role() = 'service_role');

-- Cabinet memberships
DROP POLICY IF EXISTS "cm_own"           ON cabinet_memberships;
DROP POLICY IF EXISTS "cm_service_role"  ON cabinet_memberships;
CREATE POLICY "cm_own"          ON cabinet_memberships FOR ALL USING (profile_id = auth.uid());
CREATE POLICY "cm_service_role" ON cabinet_memberships FOR ALL USING (auth.role() = 'service_role');

-- Document artefacts: via file ownership
DROP POLICY IF EXISTS "da_own"           ON document_artefacts;
DROP POLICY IF EXISTS "da_service_role"  ON document_artefacts;
CREATE POLICY "da_own" ON document_artefacts FOR ALL
    USING (file_id IN (
        SELECT f.id FROM files f
        JOIN file_cabinets fc ON fc.id = f.cabinet_id
        WHERE fc.user_id = auth.uid()
    ));
CREATE POLICY "da_service_role" ON document_artefacts FOR ALL USING (auth.role() = 'service_role');

-- Brains
DROP POLICY IF EXISTS "brains_own"           ON brains;
DROP POLICY IF EXISTS "brains_service_role"  ON brains;
CREATE POLICY "brains_own"          ON brains FOR ALL USING (user_id = auth.uid());
CREATE POLICY "brains_service_role" ON brains FOR ALL USING (auth.role() = 'service_role');

-- Brain files
DROP POLICY IF EXISTS "brain_files_own"          ON brain_files;
DROP POLICY IF EXISTS "brain_files_service_role" ON brain_files;
CREATE POLICY "brain_files_own" ON brain_files FOR ALL
    USING (brain_id IN (SELECT id FROM brains WHERE user_id = auth.uid()));
CREATE POLICY "brain_files_service_role" ON brain_files FOR ALL USING (auth.role() = 'service_role');

-- Classes: members of institute can read; teachers can write
DROP POLICY IF EXISTS "classes_institute_read"  ON classes;
DROP POLICY IF EXISTS "classes_service_role"    ON classes;
CREATE POLICY "classes_institute_read" ON classes FOR SELECT
    USING (institute_id IN (SELECT institute_id FROM institute_memberships WHERE profile_id = auth.uid()));
CREATE POLICY "classes_service_role" ON classes FOR ALL USING (auth.role() = 'service_role');

-- Class teachers / students
DROP POLICY IF EXISTS "ct_service_role" ON class_teachers;
DROP POLICY IF EXISTS "cs_service_role" ON class_students;
CREATE POLICY "ct_service_role" ON class_teachers FOR ALL USING (auth.role() = 'service_role');
CREATE POLICY "cs_service_role" ON class_students FOR ALL USING (auth.role() = 'service_role');

-- Enrollment requests
DROP POLICY IF EXISTS "er_own"           ON enrollment_requests;
DROP POLICY IF EXISTS "er_service_role"  ON enrollment_requests;
CREATE POLICY "er_own"          ON enrollment_requests FOR ALL USING (student_id = auth.uid());
CREATE POLICY "er_service_role" ON enrollment_requests FOR ALL USING (auth.role() = 'service_role');

-- Planned lessons
DROP POLICY IF EXISTS "pl_own"            ON planned_lessons;
DROP POLICY IF EXISTS "pl_collab_read"    ON planned_lessons;
DROP POLICY IF EXISTS "pl_service_role"   ON planned_lessons;
CREATE POLICY "pl_own" ON planned_lessons FOR ALL USING (created_by = auth.uid());
CREATE POLICY "pl_collab_read" ON planned_lessons FOR SELECT
    USING (id IN (SELECT planned_lesson_id FROM lesson_collaborators WHERE profile_id = auth.uid()));
CREATE POLICY "pl_service_role" ON planned_lessons FOR ALL USING (auth.role() = 'service_role');

-- Lesson collaborators
DROP POLICY IF EXISTS "lc_own"           ON lesson_collaborators;
DROP POLICY IF EXISTS "lc_service_role"  ON lesson_collaborators;
CREATE POLICY "lc_own"          ON lesson_collaborators FOR ALL USING (profile_id = auth.uid());
CREATE POLICY "lc_service_role" ON lesson_collaborators FOR ALL USING (auth.role() = 'service_role');

-- Lesson deliveries
DROP POLICY IF EXISTS "ld_own"           ON lesson_deliveries;
DROP POLICY IF EXISTS "ld_service_role"  ON lesson_deliveries;
CREATE POLICY "ld_own"          ON lesson_deliveries FOR ALL USING (delivered_by = auth.uid());
CREATE POLICY "ld_service_role" ON lesson_deliveries FOR ALL USING (auth.role() = 'service_role');

-- Curriculum topics: public read, service role write
DROP POLICY IF EXISTS "ct_public_read"    ON curriculum_topics;
DROP POLICY IF EXISTS "ct_service_role"   ON curriculum_topics;
CREATE POLICY "ct_public_read"   ON curriculum_topics FOR SELECT USING (true);
CREATE POLICY "ct_service_role"  ON curriculum_topics FOR ALL USING (auth.role() = 'service_role');

-- Exam boards: public read
DROP POLICY IF EXISTS "eb_spec_public_read" ON eb_specifications;
DROP POLICY IF EXISTS "eb_spec_service_role" ON eb_specifications;
DROP POLICY IF EXISTS "eb_exam_public_read" ON eb_exams;
DROP POLICY IF EXISTS "eb_exam_service_role" ON eb_exams;
CREATE POLICY "eb_spec_public_read"   ON eb_specifications FOR SELECT USING (true);
CREATE POLICY "eb_spec_service_role"  ON eb_specifications FOR ALL USING (auth.role() = 'service_role');
CREATE POLICY "eb_exam_public_read"   ON eb_exams FOR SELECT USING (true);
CREATE POLICY "eb_exam_service_role"  ON eb_exams FOR ALL USING (auth.role() = 'service_role');

-- CIS: users own their sessions
DROP POLICY IF EXISTS "ts_own"  ON transcription_sessions;
DROP POLICY IF EXISTS "seg_own" ON transcription_segments;
DROP POLICY IF EXISTS "ce_own"  ON canvas_events;
DROP POLICY IF EXISTS "sum_own" ON transcription_summaries;
DROP POLICY IF EXISTS "kw_own"  ON keyword_watches;
DROP POLICY IF EXISTS "ke_own"  ON keyword_events;
CREATE POLICY "ts_own"  ON transcription_sessions  FOR ALL USING (user_id = auth.uid());
CREATE POLICY "seg_own" ON transcription_segments  FOR ALL
    USING (session_id IN (SELECT id FROM transcription_sessions WHERE user_id = auth.uid()));
CREATE POLICY "ce_own"  ON canvas_events           FOR ALL USING (user_id = auth.uid());
CREATE POLICY "sum_own" ON transcription_summaries FOR ALL USING (user_id = auth.uid());
CREATE POLICY "kw_own"  ON keyword_watches         FOR ALL USING (user_id = auth.uid());
CREATE POLICY "ke_own"  ON keyword_events          FOR ALL
    USING (session_id IN (SELECT id FROM transcription_sessions WHERE user_id = auth.uid()));
