create extension if not exists ltree;

create table if not exists public.content_entries (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  region_id uuid references public.regions(id) on delete set null,
  legacy_id text,
  entry_key text not null,
  name text not null,
  entry_type text not null default 'question_practice'
    check (entry_type in ('question_practice', 'vocabulary', 'handbook', 'scoreline', 'resource', 'ai_report', 'custom')),
  icon text,
  route text,
  description text,
  visibility text not null default 'public'
    check (visibility in ('public', 'members', 'svip', 'hidden')),
  access_rules jsonb not null default '{}'::jsonb,
  layout_config jsonb not null default '{}'::jsonb,
  sort_order integer not null default 0,
  is_active boolean not null default true,
  created_by uuid references public.platform_users(id) on delete set null,
  updated_by uuid references public.platform_users(id) on delete set null,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, entry_key),
  unique (tenant_id, legacy_id)
);

create table if not exists public.content_nodes (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  entry_id uuid not null references public.content_entries(id) on delete cascade,
  region_id uuid references public.regions(id) on delete set null,
  parent_id uuid references public.content_nodes(id) on delete cascade,
  legacy_id text,
  node_key text,
  name text not null,
  node_type text not null default 'category'
    check (node_type in ('category', 'subject', 'chapter', 'paper', 'school', 'major', 'exam_target', 'resource_group', 'custom')),
  marker_type text
    check (marker_type is null or marker_type in ('school', 'major', 'subject', 'exam_track', 'course_package', 'sales_intent', 'custom')),
  marker_config jsonb not null default '{}'::jsonb,
  path ltree,
  depth integer not null default 0 check (depth >= 0),
  sort_order integer not null default 0,
  is_active boolean not null default true,
  is_selectable boolean not null default true,
  is_leaf boolean not null default false,
  metadata jsonb not null default '{}'::jsonb,
  created_by uuid references public.platform_users(id) on delete set null,
  updated_by uuid references public.platform_users(id) on delete set null,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, legacy_id),
  unique (tenant_id, entry_id, node_key)
);

create table if not exists public.question_collections (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  region_id uuid references public.regions(id) on delete set null,
  entry_id uuid references public.content_entries(id) on delete set null,
  node_id uuid references public.content_nodes(id) on delete set null,
  subject_id uuid references public.subjects(id) on delete set null,
  category_id uuid references public.categories(id) on delete set null,
  question_bank_id uuid references public.question_banks(id) on delete set null,
  legacy_id text,
  name text not null,
  collection_type text not null default 'dynamic'
    check (collection_type in ('dynamic', 'manual', 'paper', 'chapter', 'mock_exam')),
  source_type text not null default 'filters'
    check (source_type in ('filters', 'manual_questions', 'node_descendants', 'category', 'subject', 'question_bank')),
  filters jsonb not null default '{}'::jsonb,
  question_count integer not null default 0 check (question_count >= 0),
  total_score numeric(8,2),
  duration_minutes integer check (duration_minutes is null or duration_minutes > 0),
  status text not null default 'active' check (status in ('draft', 'active', 'archived')),
  sort_order integer not null default 0,
  metadata jsonb not null default '{}'::jsonb,
  created_by uuid references public.platform_users(id) on delete set null,
  updated_by uuid references public.platform_users(id) on delete set null,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, legacy_id)
);

create table if not exists public.question_collection_items (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  collection_id uuid not null references public.question_collections(id) on delete cascade,
  question_id uuid not null references public.questions(id) on delete cascade,
  section_key text,
  sort_order integer not null default 0,
  score numeric(8,2),
  required boolean not null default true,
  metadata jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, collection_id, question_id)
);

create table if not exists public.practice_blueprints (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  region_id uuid references public.regions(id) on delete set null,
  entry_id uuid references public.content_entries(id) on delete set null,
  node_id uuid references public.content_nodes(id) on delete set null,
  collection_id uuid references public.question_collections(id) on delete set null,
  legacy_id text,
  name text not null,
  mode text not null check (mode in ('sequential', 'random', 'mock_exam', 'paper', 'wrong_review', 'favorite_review')),
  assembly_type text not null default 'collection'
    check (assembly_type in ('collection', 'node_descendants', 'manual', 'filters')),
  question_limit integer check (question_limit is null or question_limit > 0),
  duration_minutes integer check (duration_minutes is null or duration_minutes > 0),
  total_score numeric(8,2),
  pass_score numeric(8,2),
  sections jsonb not null default '[]'::jsonb,
  rules jsonb not null default '{}'::jsonb,
  status text not null default 'active' check (status in ('draft', 'active', 'archived')),
  sort_order integer not null default 0,
  created_by uuid references public.platform_users(id) on delete set null,
  updated_by uuid references public.platform_users(id) on delete set null,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, legacy_id)
);

alter table public.questions
  add column if not exists entry_id uuid references public.content_entries(id) on delete set null,
  add column if not exists content_node_id uuid references public.content_nodes(id) on delete set null,
  add column if not exists primary_collection_id uuid references public.question_collections(id) on delete set null,
  add column if not exists exam_markers jsonb not null default '{}'::jsonb;

alter table public.content_assets
  add column if not exists entry_id uuid references public.content_entries(id) on delete set null,
  add column if not exists content_node_id uuid references public.content_nodes(id) on delete set null;

alter table public.content_import_jobs
  add column if not exists target_entry_id uuid references public.content_entries(id) on delete set null,
  add column if not exists target_content_node_id uuid references public.content_nodes(id) on delete set null,
  add column if not exists target_collection_id uuid references public.question_collections(id) on delete set null;

alter table public.practice_sessions
  add column if not exists blueprint_id uuid references public.practice_blueprints(id) on delete set null,
  add column if not exists collection_id uuid references public.question_collections(id) on delete set null,
  add column if not exists entry_id uuid references public.content_entries(id) on delete set null,
  add column if not exists content_node_id uuid references public.content_nodes(id) on delete set null,
  add column if not exists question_ids jsonb not null default '[]'::jsonb,
  add column if not exists question_count integer not null default 0,
  add column if not exists duration_minutes integer,
  add column if not exists total_score numeric(8,2),
  add column if not exists expires_at timestamptz;

create index if not exists idx_content_entries_tenant_region
  on public.content_entries(tenant_id, region_id, entry_type, is_active, sort_order);
create index if not exists idx_content_nodes_tenant_entry_parent
  on public.content_nodes(tenant_id, entry_id, parent_id, sort_order);
create index if not exists idx_content_nodes_path
  on public.content_nodes using gist(path);
create index if not exists idx_content_nodes_marker
  on public.content_nodes(tenant_id, marker_type)
  where marker_type is not null;
create index if not exists idx_question_collections_node
  on public.question_collections(tenant_id, node_id, status, sort_order);
create index if not exists idx_question_collection_items_collection
  on public.question_collection_items(tenant_id, collection_id, section_key, sort_order);
create index if not exists idx_practice_blueprints_scope
  on public.practice_blueprints(tenant_id, node_id, collection_id, mode, status, sort_order);
create index if not exists idx_questions_navigation
  on public.questions(tenant_id, entry_id, content_node_id, primary_collection_id, status);

do $$
declare
  table_name text;
begin
  foreach table_name in array array[
    'content_entries', 'content_nodes', 'question_collections',
    'question_collection_items', 'practice_blueprints'
  ]
  loop
    execute format('alter table public.%I enable row level security', table_name);
    execute format('drop policy if exists tenant_isolation on public.%I', table_name);
    execute format(
      'create policy tenant_isolation on public.%I for all using (tenant_id = app.current_tenant_id() or app.is_platform_admin()) with check (tenant_id = app.current_tenant_id() or app.is_platform_admin())',
      table_name
    );
    execute format('drop trigger if exists set_updated_at on public.%I', table_name);
    execute format('create trigger set_updated_at before update on public.%I for each row execute function app.touch_updated_at()', table_name);
  end loop;
end $$;
