PRAGMA foreign_keys = ON;

  CREATE TABLE IF NOT EXISTS schema_metadata (
    id INTEGER PRIMARY KEY CHECK (id = 1),
    schema_version INTEGER NOT NULL DEFAULT 1,
    app_version INTEGER NOT NULL DEFAULT 1,
    self_registration_enabled INTEGER NOT NULL DEFAULT 0 CHECK (self_registration_enabled IN (0, 1)),
    created_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS organization (
    id INTEGER PRIMARY KEY CHECK (id = 1),
    name TEXT NOT NULL,
    code TEXT NOT NULL,
    phone TEXT NOT NULL,
    address TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS schools (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL UNIQUE,
    code TEXT NOT NULL UNIQUE,
    address TEXT,
    is_source_school INTEGER NOT NULL DEFAULT 1 CHECK (is_source_school IN (0, 1)),
    is_admission_school INTEGER NOT NULL DEFAULT 1 CHECK (is_admission_school IN (0, 1)),
    active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1))
  ) STRICT;

  CREATE TABLE IF NOT EXISTS school_classes (
    id TEXT PRIMARY KEY,
    school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    grade TEXT NOT NULL,
    active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)),
    UNIQUE (school_id, name)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS school_student_partitions (
    school_id TEXT PRIMARY KEY REFERENCES schools(id) ON DELETE CASCADE,
    partition_key TEXT NOT NULL UNIQUE,
    students_table TEXT NOT NULL UNIQUE,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    username TEXT NOT NULL UNIQUE,
    candidate_number TEXT UNIQUE,
    password_hash TEXT NOT NULL,
    role TEXT NOT NULL CHECK (role IN ('admin', 'candidate', 'admission_school')),
    admin_level TEXT CHECK (admin_level IN ('super', 'school', 'class')),
    school_id TEXT REFERENCES schools(id) ON DELETE SET NULL,
    class_id TEXT REFERENCES school_classes(id) ON DELETE SET NULL,
    active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)),
    must_change_password INTEGER NOT NULL DEFAULT 0 CHECK (must_change_password IN (0, 1)),
    totp_enabled INTEGER NOT NULL DEFAULT 0 CHECK (totp_enabled IN (0, 1)),
    totp_secret_encrypted TEXT,
    totp_recovery_codes TEXT NOT NULL DEFAULT '[]',
    totp_last_used_step INTEGER,
    archived_at TEXT,
    archived_by TEXT REFERENCES users(id) ON DELETE RESTRICT,
    display_name TEXT NOT NULL,
    created_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS candidate_profiles (
    id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    gender TEXT,
    id_number TEXT NOT NULL UNIQUE,
    phone TEXT NOT NULL,
    email TEXT,
    school TEXT,
    grade TEXT,
    school_id TEXT REFERENCES schools(id) ON DELETE SET NULL,
    class_id TEXT REFERENCES school_classes(id) ON DELETE SET NULL,
    province_code TEXT,
    province_name TEXT,
    city_code TEXT,
    city_name TEXT,
    district_code TEXT,
    district_name TEXT,
    address TEXT,
    emergency_contact TEXT,
    emergency_phone TEXT,
    native_place TEXT,
    birth_date TEXT,
    ethnicity TEXT,
    postal_code TEXT,
    guardian_name TEXT,
    guardian_phone TEXT,
    specialty_category TEXT,
    specialty_type TEXT,
    specialty_types TEXT NOT NULL DEFAULT '[]',
    specialty_certificate TEXT,
    policy_eligibility TEXT,
    profile_completed INTEGER NOT NULL DEFAULT 0 CHECK (profile_completed IN (0, 1)),
    status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')),
    review_note TEXT,
    reviewed_at TEXT,
    reviewer_id TEXT REFERENCES users(id) ON DELETE SET NULL,
    updated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS notices (
    id TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    summary TEXT NOT NULL,
    content TEXT NOT NULL,
    category TEXT NOT NULL,
    pinned INTEGER NOT NULL DEFAULT 0 CHECK (pinned IN (0, 1)),
    status TEXT NOT NULL CHECK (status IN ('draft', 'published')),
    publish_at TEXT,
    created_at TEXT,
    author TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS exams (
    id TEXT PRIMARY KEY,
    code TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    description TEXT NOT NULL,
    registration_start TEXT NOT NULL,
    registration_end TEXT NOT NULL,
    exam_start TEXT NOT NULL,
    exam_end TEXT NOT NULL,
    admit_download_start TEXT NOT NULL,
    admit_download_end TEXT NOT NULL,
    location TEXT NOT NULL,
    pass_policy TEXT NOT NULL DEFAULT 'rank_percent' CHECK (pass_policy IN ('fixed_score', 'score_ratio', 'rank_percent', 'subject_scores', 'none')),
    pass_value REAL NOT NULL DEFAULT 60,
    status TEXT NOT NULL CHECK (status IN ('draft', 'published', 'closed')),
    archived_at TEXT,
    archived_by TEXT REFERENCES users(id) ON DELETE RESTRICT,
    created_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS exam_data_partitions (
    exam_id TEXT PRIMARY KEY REFERENCES exams(id) ON DELETE CASCADE,
    partition_key TEXT NOT NULL UNIQUE,
    candidates_table TEXT NOT NULL UNIQUE,
    admissions_table TEXT NOT NULL UNIQUE,
    results_table TEXT NOT NULL UNIQUE,
    centers_table TEXT NOT NULL UNIQUE,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS exam_subjects (
    id TEXT PRIMARY KEY,
    exam_id TEXT NOT NULL REFERENCES exams(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    subject_date TEXT NOT NULL,
    start_time TEXT NOT NULL,
    end_time TEXT NOT NULL,
    fee REAL NOT NULL DEFAULT 0,
    full_score REAL NOT NULL DEFAULT 150,
    pass_score REAL NOT NULL DEFAULT 90,
    pass_rule TEXT NOT NULL DEFAULT 'fixed_score' CHECK (pass_rule IN ('fixed_score', 'score_ratio', 'none')),
    pass_value REAL NOT NULL DEFAULT 90,
    position INTEGER NOT NULL,
    UNIQUE (exam_id, position)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS registrations (
    id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    exam_id TEXT NOT NULL REFERENCES exams(id) ON DELETE CASCADE,
    status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')),
    payment_status TEXT NOT NULL CHECK (payment_status IN ('unpaid', 'paid')),
    paid_at TEXT,
    paid_by TEXT REFERENCES users(id) ON DELETE SET NULL,
    created_at TEXT NOT NULL,
    reviewed_at TEXT,
    review_note TEXT,
    registration_number TEXT,
    number_rule_id TEXT,
    feature_score REAL NOT NULL DEFAULT 0 CHECK (feature_score >= 0),
    UNIQUE (user_id, exam_id)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS registration_subjects (
    registration_id TEXT NOT NULL REFERENCES registrations(id) ON DELETE CASCADE,
    subject_id TEXT NOT NULL REFERENCES exam_subjects(id) ON DELETE CASCADE,
    PRIMARY KEY (registration_id, subject_id)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS admission_number_rules (
    id TEXT PRIMARY KEY,
    code TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    description TEXT NOT NULL,
    separator TEXT NOT NULL DEFAULT '',
    segments_json TEXT NOT NULL,
    example TEXT NOT NULL,
    active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)),
    created_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS exam_arrangement_plans (
    id TEXT PRIMARY KEY,
    exam_id TEXT NOT NULL UNIQUE REFERENCES exams(id) ON DELETE CASCADE,
    number_rule_id TEXT NOT NULL REFERENCES admission_number_rules(id),
    mixing_scope TEXT NOT NULL CHECK (mixing_scope IN ('class', 'school', 'district', 'city', 'province')),
    random_seed TEXT NOT NULL,
    candidate_count INTEGER NOT NULL CHECK (candidate_count >= 0),
    center_count INTEGER NOT NULL CHECK (center_count >= 0),
    subject_assignment_count INTEGER NOT NULL CHECK (subject_assignment_count >= 0),
    subject_combination_count INTEGER NOT NULL CHECK (subject_combination_count >= 0),
    same_school_center_rate REAL NOT NULL,
    warnings_json TEXT NOT NULL,
    generated_by TEXT REFERENCES users(id) ON DELETE SET NULL,
    generated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS admit_cards (
    registration_id TEXT PRIMARY KEY REFERENCES registrations(id) ON DELETE CASCADE,
    plan_id TEXT NOT NULL REFERENCES exam_arrangement_plans(id) ON DELETE CASCADE,
    card_number TEXT NOT NULL UNIQUE,
    center_id TEXT REFERENCES test_centers(id) ON DELETE SET NULL,
    test_center TEXT NOT NULL,
    center_code TEXT NOT NULL,
    center_address TEXT NOT NULL,
    generated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS admit_card_subjects (
    registration_id TEXT NOT NULL REFERENCES admit_cards(registration_id) ON DELETE CASCADE,
    subject_id TEXT NOT NULL REFERENCES exam_subjects(id) ON DELETE CASCADE,
    room_id TEXT REFERENCES test_rooms(id) ON DELETE SET NULL,
    room TEXT NOT NULL,
    room_code TEXT NOT NULL,
    exam_room_code TEXT NOT NULL,
    building TEXT NOT NULL,
    floor TEXT NOT NULL,
    seat TEXT NOT NULL,
    subject_signature TEXT NOT NULL,
    PRIMARY KEY (registration_id, subject_id),
    UNIQUE (subject_id, room_id, seat)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS results (
    id TEXT PRIMARY KEY,
    registration_id TEXT NOT NULL REFERENCES registrations(id) ON DELETE CASCADE,
    subject_id TEXT NOT NULL REFERENCES exam_subjects(id) ON DELETE CASCADE,
    score REAL NOT NULL CHECK (score >= 0),
    grade TEXT NOT NULL,
    published INTEGER NOT NULL DEFAULT 0 CHECK (published IN (0, 1)),
    updated_at TEXT,
    published_at TEXT,
    UNIQUE (registration_id, subject_id)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS result_statistics (
    result_id TEXT PRIMARY KEY REFERENCES results(id) ON DELETE CASCADE,
    subject_rank INTEGER,
    cohort_size INTEGER NOT NULL DEFAULT 0 CHECK (cohort_size >= 0),
    rank_percent REAL,
    grade TEXT NOT NULL,
    qualified INTEGER CHECK (qualified IN (0, 1)),
    pass_score REAL,
    cutoff_rank INTEGER,
    calculated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS registration_result_summaries (
    registration_id TEXT PRIMARY KEY REFERENCES registrations(id) ON DELETE CASCADE,
    exam_id TEXT NOT NULL REFERENCES exams(id) ON DELETE CASCADE,
    published_subject_count INTEGER NOT NULL DEFAULT 0 CHECK (published_subject_count >= 0),
    subject_count INTEGER NOT NULL DEFAULT 0 CHECK (subject_count >= 0),
    complete INTEGER NOT NULL DEFAULT 0 CHECK (complete IN (0, 1)),
    total_score REAL NOT NULL DEFAULT 0,
    full_score REAL NOT NULL DEFAULT 0,
    score_ratio REAL NOT NULL DEFAULT 0,
    total_rank INTEGER,
    cohort_size INTEGER NOT NULL DEFAULT 0 CHECK (cohort_size >= 0),
    rank_percent REAL,
    qualified INTEGER CHECK (qualified IN (0, 1)),
    cutoff_rank INTEGER,
    calculated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS test_centers (
    id TEXT PRIMARY KEY,
    school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
    code TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    province_code TEXT NOT NULL,
    province_name TEXT NOT NULL,
    city_code TEXT NOT NULL,
    city_name TEXT NOT NULL,
    district_code TEXT NOT NULL,
    district_name TEXT NOT NULL,
    address TEXT NOT NULL,
    contact TEXT,
    manager_name TEXT,
    manager_phone TEXT,
    emergency_phone TEXT,
    gate_open_time TEXT,
    transport TEXT,
    status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')),
    notes TEXT,
    rooms TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    UNIQUE (school_id, name)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS test_rooms (
    id TEXT PRIMARY KEY,
    center_id TEXT NOT NULL REFERENCES test_centers(id) ON DELETE CASCADE,
    code TEXT NOT NULL,
    name TEXT NOT NULL,
    building TEXT NOT NULL,
    floor TEXT,
    capacity INTEGER NOT NULL CHECK (capacity > 0),
    seat_plan TEXT,
    seat_start INTEGER NOT NULL DEFAULT 1 CHECK (seat_start > 0),
    seat_end INTEGER NOT NULL CHECK (seat_end >= seat_start),
    room_type TEXT NOT NULL CHECK (room_type IN ('standard', 'computer', 'accessible', 'spare')),
    status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')),
    notes TEXT,
    UNIQUE (center_id, code)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS center_change_requests (
    id TEXT PRIMARY KEY,
    center_id TEXT REFERENCES test_centers(id) ON DELETE SET NULL,
    school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
    request_type TEXT NOT NULL CHECK (request_type IN ('create', 'update')),
    code TEXT NOT NULL,
    name TEXT NOT NULL,
    province_code TEXT NOT NULL,
    province_name TEXT NOT NULL,
    city_code TEXT NOT NULL,
    city_name TEXT NOT NULL,
    district_code TEXT NOT NULL,
    district_name TEXT NOT NULL,
    address TEXT NOT NULL,
    contact TEXT,
    manager_name TEXT,
    manager_phone TEXT,
    emergency_phone TEXT,
    gate_open_time TEXT,
    transport TEXT,
    center_status TEXT NOT NULL CHECK (center_status IN ('active', 'inactive')),
    notes TEXT,
    status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')),
    review_note TEXT,
    requested_by TEXT REFERENCES users(id) ON DELETE SET NULL,
    created_at TEXT NOT NULL,
    reviewed_at TEXT
  ) STRICT;

  CREATE TABLE IF NOT EXISTS center_change_rooms (
    id TEXT PRIMARY KEY,
    request_id TEXT NOT NULL REFERENCES center_change_requests(id) ON DELETE CASCADE,
    room_id TEXT,
    code TEXT NOT NULL,
    name TEXT NOT NULL,
    building TEXT NOT NULL,
    floor TEXT,
    capacity INTEGER NOT NULL CHECK (capacity > 0),
    seat_plan TEXT,
    seat_start INTEGER NOT NULL DEFAULT 1,
    seat_end INTEGER NOT NULL,
    room_type TEXT NOT NULL CHECK (room_type IN ('standard', 'computer', 'accessible', 'spare')),
    status TEXT NOT NULL CHECK (status IN ('active', 'inactive')),
    notes TEXT,
    UNIQUE (request_id, code)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS number_rules (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    separator TEXT NOT NULL DEFAULT '',
    active INTEGER NOT NULL DEFAULT 0 CHECK (active IN (0, 1)),
    created_by TEXT REFERENCES users(id) ON DELETE SET NULL,
    updated_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS number_rule_segments (
    id TEXT PRIMARY KEY,
    rule_id TEXT NOT NULL REFERENCES number_rules(id) ON DELETE CASCADE,
    position INTEGER NOT NULL,
    type TEXT NOT NULL CHECK (type IN ('year', 'school_code', 'gender', 'sequence', 'literal')),
    value TEXT,
    width INTEGER NOT NULL DEFAULT 0,
    UNIQUE (rule_id, position)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS candidate_account_batches (
    id TEXT PRIMARY KEY,
    school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
    requested_by TEXT REFERENCES users(id) ON DELETE SET NULL,
    status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')),
    review_note TEXT,
    created_at TEXT NOT NULL,
    reviewed_at TEXT
  ) STRICT;

  CREATE TABLE IF NOT EXISTS candidate_account_batch_items (
    id TEXT PRIMARY KEY,
    batch_id TEXT NOT NULL REFERENCES candidate_account_batches(id) ON DELETE CASCADE,
    class_id TEXT NOT NULL REFERENCES school_classes(id) ON DELETE RESTRICT,
    position INTEGER NOT NULL,
    candidate_number TEXT UNIQUE,
    initial_password TEXT,
    user_id TEXT UNIQUE REFERENCES users(id) ON DELETE SET NULL,
    created_at TEXT,
    UNIQUE (batch_id, position)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS workflow_definitions (
    id TEXT PRIMARY KEY,
    business_type TEXT NOT NULL CHECK (business_type IN ('profile_change', 'registration_review', 'center_change', 'candidate_account_batch', 'score_appeal')),
    name TEXT NOT NULL,
    active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)),
    updated_by TEXT REFERENCES users(id) ON DELETE SET NULL,
    updated_at TEXT NOT NULL,
    UNIQUE (business_type, active)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS workflow_steps (
    id TEXT PRIMARY KEY,
    workflow_id TEXT NOT NULL REFERENCES workflow_definitions(id) ON DELETE CASCADE,
    position INTEGER NOT NULL,
    name TEXT NOT NULL,
    admin_level TEXT NOT NULL CHECK (admin_level IN ('class', 'school', 'super')),
    UNIQUE (workflow_id, position)
  ) STRICT;

  CREATE TABLE IF NOT EXISTS workflow_instances (
    id TEXT PRIMARY KEY,
    workflow_id TEXT NOT NULL REFERENCES workflow_definitions(id) ON DELETE RESTRICT,
    business_type TEXT NOT NULL,
    business_id TEXT NOT NULL,
    status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')),
    current_step INTEGER NOT NULL DEFAULT 1,
    assignee_id TEXT REFERENCES users(id) ON DELETE SET NULL,
    created_at TEXT NOT NULL,
    completed_at TEXT
  ) STRICT;

  CREATE TABLE IF NOT EXISTS workflow_actions (
    id TEXT PRIMARY KEY,
    instance_id TEXT NOT NULL REFERENCES workflow_instances(id) ON DELETE CASCADE,
    actor_id TEXT REFERENCES users(id) ON DELETE SET NULL,
    action TEXT NOT NULL CHECK (action IN ('submit', 'approve', 'reject', 'transfer', 'return', 'supervise')),
    note TEXT,
    from_assignee_id TEXT REFERENCES users(id) ON DELETE SET NULL,
    to_assignee_id TEXT REFERENCES users(id) ON DELETE SET NULL,
    created_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS audit_logs (
    id TEXT PRIMARY KEY,
    actor_id TEXT REFERENCES users(id) ON DELETE SET NULL,
    action TEXT NOT NULL,
    detail TEXT NOT NULL,
    created_at TEXT NOT NULL
  ) STRICT;

  CREATE TABLE IF NOT EXISTS admission_records (
    id TEXT PRIMARY KEY,
    kind TEXT NOT NULL CHECK (kind IN ('setting', 'plan', 'preference', 'placement', 'notification', 'indicator_qualification', 'qualification_publication', 'cutoff_publication')),
    exam_id TEXT NOT NULL REFERENCES exams(id) ON DELETE CASCADE,
    user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
    school_id TEXT REFERENCES schools(id) ON DELETE CASCADE,
    status TEXT NOT NULL,
    payload_json TEXT NOT NULL DEFAULT '{}',
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
  ) STRICT;

  CREATE INDEX IF NOT EXISTS idx_profiles_status ON candidate_profiles(status);
  CREATE INDEX IF NOT EXISTS idx_users_archive_scope ON users(role, school_id, class_id, archived_at);
  CREATE INDEX IF NOT EXISTS idx_exams_archive ON exams(archived_at, exam_end);
  CREATE INDEX IF NOT EXISTS idx_profiles_scope ON candidate_profiles(school_id, class_id, status);
  CREATE INDEX IF NOT EXISTS idx_notices_status_publish ON notices(status, publish_at);
  CREATE INDEX IF NOT EXISTS idx_exams_status_registration ON exams(status, registration_start, registration_end);
  CREATE INDEX IF NOT EXISTS idx_subjects_exam ON exam_subjects(exam_id, position);
  CREATE INDEX IF NOT EXISTS idx_registrations_status ON registrations(status);
  CREATE INDEX IF NOT EXISTS idx_registrations_payment ON registrations(payment_status, paid_at);
  CREATE INDEX IF NOT EXISTS idx_registrations_exam ON registrations(exam_id);
  CREATE INDEX IF NOT EXISTS idx_arrangement_plans_exam ON exam_arrangement_plans(exam_id, generated_at);
  CREATE INDEX IF NOT EXISTS idx_admit_subjects_room ON admit_card_subjects(subject_id, room_id, seat);
  CREATE INDEX IF NOT EXISTS idx_workflow_inbox ON workflow_instances(status, assignee_id, business_type);
  CREATE UNIQUE INDEX IF NOT EXISTS uq_test_centers_code ON test_centers(code);
  CREATE INDEX IF NOT EXISTS idx_rooms_center ON test_rooms(center_id, status, code);
  CREATE INDEX IF NOT EXISTS idx_center_changes_school ON center_change_requests(school_id, status, created_at);
  CREATE INDEX IF NOT EXISTS idx_account_batches_school ON candidate_account_batches(school_id, status, created_at);
  CREATE INDEX IF NOT EXISTS idx_account_batch_items ON candidate_account_batch_items(batch_id, class_id, position);
  CREATE INDEX IF NOT EXISTS idx_results_registration ON results(registration_id, published);
  CREATE INDEX IF NOT EXISTS idx_result_summaries_exam ON registration_result_summaries(exam_id, complete, qualified);
  CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_logs(created_at);
  CREATE INDEX IF NOT EXISTS idx_admission_records_lookup ON admission_records(kind, exam_id, school_id, user_id, status);

  CREATE TRIGGER IF NOT EXISTS trg_results_lock_archived_insert
  BEFORE INSERT ON results
  WHEN EXISTS (
    SELECT 1 FROM registrations registration
    JOIN exams exam ON exam.id = registration.exam_id
    WHERE registration.id = NEW.registration_id AND exam.archived_at IS NOT NULL
  )
  BEGIN
    SELECT RAISE(ABORT, '归档考试成绩已永久锁定');
  END;

  CREATE TRIGGER IF NOT EXISTS trg_results_lock_archived_update
  BEFORE UPDATE ON results
  WHEN EXISTS (
    SELECT 1 FROM registrations registration
    JOIN exams exam ON exam.id = registration.exam_id
    WHERE registration.id IN (OLD.registration_id, NEW.registration_id) AND exam.archived_at IS NOT NULL
  )
  BEGIN
    SELECT RAISE(ABORT, '归档考试成绩已永久锁定');
  END;

  CREATE TRIGGER IF NOT EXISTS trg_results_lock_archived_delete
  BEFORE DELETE ON results
  WHEN EXISTS (
    SELECT 1 FROM registrations registration
    JOIN exams exam ON exam.id = registration.exam_id
    WHERE registration.id = OLD.registration_id AND exam.archived_at IS NOT NULL
  )
  BEGIN
    SELECT RAISE(ABORT, '归档考试成绩已永久锁定');
  END;
