create table if not exists public.point_activity_tasks (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  legacy_id text,
  code citext not null,
  title text not null,
  description text,
  task_type text not null default 'manual'
    check (task_type in (
      'daily_check_in',
      'feedback_submit',
      'feedback_resolved',
      'practice_complete',
      'vocabulary_review',
      'mock_exam_submit',
      'manual'
    )),
  reward_points integer not null default 0 check (reward_points > 0 and reward_points <= 100000),
  claim_limit_per_user integer not null default 1 check (claim_limit_per_user > 0 and claim_limit_per_user <= 100000),
  period_type text not null default 'once'
    check (period_type in ('once', 'daily', 'weekly', 'monthly', 'unlimited')),
  valid_from timestamptz,
  valid_to timestamptz,
  status text not null default 'active'
    check (status in ('active', 'disabled', '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,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, code),
  unique (tenant_id, legacy_id)
);

comment on table public.point_activity_tasks is
  'Tenant-scoped score activity tasks configured by marketing/operations teams.';

create table if not exists public.user_point_activity_claims (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  user_id uuid not null references public.platform_users(id) on delete cascade,
  task_id uuid not null references public.point_activity_tasks(id) on delete cascade,
  period_key text not null,
  score_event_id uuid references public.user_score_events(id) on delete set null,
  source_type text,
  source_id uuid,
  status text not null default 'claimed'
    check (status in ('claimed', 'cancelled')),
  metadata jsonb not null default '{}'::jsonb,
  claimed_at timestamptz not null default now(),
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, user_id, task_id, period_key)
);

comment on table public.user_point_activity_claims is
  'Idempotent student claim records for activity rewards.';

create table if not exists public.point_exchange_items (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  legacy_id text,
  code citext not null,
  title text not null,
  description text,
  cost_points integer not null check (cost_points > 0 and cost_points <= 10000000),
  item_type text not null default 'manual'
    check (item_type in ('coupon', 'manual', 'asset', 'custom')),
  coupon_id uuid references public.coupons(id) on delete set null,
  asset_id uuid references public.content_assets(id) on delete set null,
  stock_total integer check (stock_total is null or stock_total >= 0),
  stock_used integer not null default 0 check (stock_used >= 0),
  per_user_limit integer not null default 1 check (per_user_limit > 0 and per_user_limit <= 100000),
  valid_from timestamptz,
  valid_to timestamptz,
  status text not null default 'active'
    check (status in ('active', 'disabled', '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,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, code),
  unique (tenant_id, legacy_id),
  check (stock_total is null or stock_used <= stock_total)
);

comment on table public.point_exchange_items is
  'Tenant-scoped score exchange catalog. Coupon fulfillment is supported, activation-code fulfillment is intentionally kept out of the first contract.';

create table if not exists public.user_point_exchange_orders (
  id uuid primary key default gen_random_uuid(),
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  user_id uuid not null references public.platform_users(id) on delete cascade,
  item_id uuid not null references public.point_exchange_items(id) on delete restrict,
  status text not null default 'completed'
    check (status in ('completed', 'pending_fulfillment', 'cancelled')),
  cost_points integer not null check (cost_points > 0),
  score_event_id uuid references public.user_score_events(id) on delete set null,
  coupon_redemption_id uuid references public.coupon_redemptions(id) on delete set null,
  asset_id uuid references public.content_assets(id) on delete set null,
  idempotency_key text,
  metadata jsonb not null default '{}'::jsonb,
  exchanged_at timestamptz not null default now(),
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (tenant_id, user_id, idempotency_key)
);

comment on table public.user_point_exchange_orders is
  'Student score exchange orders. Each completed order has a negative redeem_cost ledger event.';

create index if not exists idx_point_activity_tasks_tenant_status
  on public.point_activity_tasks(tenant_id, status, sort_order, created_at desc);

create index if not exists idx_point_activity_claims_user
  on public.user_point_activity_claims(tenant_id, user_id, claimed_at desc);

create index if not exists idx_point_activity_claims_task
  on public.user_point_activity_claims(tenant_id, task_id, claimed_at desc);

create index if not exists idx_point_exchange_items_tenant_status
  on public.point_exchange_items(tenant_id, status, sort_order, created_at desc);

create index if not exists idx_point_exchange_orders_user
  on public.user_point_exchange_orders(tenant_id, user_id, exchanged_at desc);

create index if not exists idx_point_exchange_orders_item
  on public.user_point_exchange_orders(tenant_id, item_id, exchanged_at desc);

alter table public.point_activity_tasks enable row level security;
alter table public.user_point_activity_claims enable row level security;
alter table public.point_exchange_items enable row level security;
alter table public.user_point_exchange_orders enable row level security;

drop policy if exists tenant_isolation on public.point_activity_tasks;
create policy tenant_isolation on public.point_activity_tasks
  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());

drop policy if exists tenant_isolation on public.user_point_activity_claims;
create policy tenant_isolation on public.user_point_activity_claims
  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());

drop policy if exists tenant_isolation on public.point_exchange_items;
create policy tenant_isolation on public.point_exchange_items
  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());

drop policy if exists tenant_isolation on public.user_point_exchange_orders;
create policy tenant_isolation on public.user_point_exchange_orders
  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());

drop trigger if exists set_updated_at on public.point_activity_tasks;
create trigger set_updated_at
  before update on public.point_activity_tasks
  for each row execute function app.touch_updated_at();

drop trigger if exists set_updated_at on public.user_point_activity_claims;
create trigger set_updated_at
  before update on public.user_point_activity_claims
  for each row execute function app.touch_updated_at();

drop trigger if exists set_updated_at on public.point_exchange_items;
create trigger set_updated_at
  before update on public.point_exchange_items
  for each row execute function app.touch_updated_at();

drop trigger if exists set_updated_at on public.user_point_exchange_orders;
create trigger set_updated_at
  before update on public.user_point_exchange_orders
  for each row execute function app.touch_updated_at();
