create extension if not exists pgcrypto;

create table if not exists public.distillation_requests (
    id uuid primary key default gen_random_uuid (),
    source_dataset text not null,
    student_model text not null,
    submitter_name text null,
    additional_notes text not null default '',
    upvotes integer not null default 0,
    voted_ips text [] not null default '{}',
    owner_id text not null,
    created_at timestamptz not null default now(),
    status text not null default 'pending'
);

create table if not exists public.dataset_requests (
    id uuid primary key default gen_random_uuid (),
    source_model text not null,
    submitter_name text null,
    dataset_size text not null default '250x',
    reasoning_depth text not null default 'high',
    topics text [] not null default '{}',
    additional_notes text not null default '',
    upvotes integer not null default 0,
    voted_ips text [] not null default '{}',
    owner_id text not null,
    created_at timestamptz not null default now(),
    status text not null default 'pending'
);

alter table public.distillation_requests
add column if not exists submitter_name text;

alter table public.dataset_requests
add column if not exists submitter_name text;

create table if not exists public.request_comments (
    id uuid primary key default gen_random_uuid (),
    request_type text not null check (
        request_type in ('distillation', 'dataset')
    ),
    request_id uuid not null,
    body text not null,
    author text not null,
    role text not null check (role in ('admin', 'user')),
    owner_id text not null,
    created_at timestamptz not null default now(),
    edited_at timestamptz null
);

create index if not exists idx_request_comments_request on public.request_comments (request_type, request_id);

create index if not exists idx_request_comments_created_at on public.request_comments (created_at);

create or replace function public.toggle_upvote_distillation(request_id uuid, voter_ip text)
returns table(success boolean, upvotes integer, action text)
language plpgsql
security definer
as $$
declare
    current_ips text[];
    current_upvotes integer;
begin
    select r.voted_ips, r.upvotes
    into current_ips, current_upvotes
    from public.distillation_requests r
    where r.id = request_id
    for update;

    if not found then
        success := false;
        upvotes := 0;
        action := null;
        return next;
        return;
    end if;

    if voter_ip = any(current_ips) then
        update public.distillation_requests as r
        set
            voted_ips = array_remove(r.voted_ips, voter_ip),
            upvotes = greatest(r.upvotes - 1, 0)
        where r.id = request_id
        returning r.upvotes into upvotes;

        success := true;
        action := 'unvoted';
        return next;
        return;
    end if;

    update public.distillation_requests as r
    set
        voted_ips = array_append(r.voted_ips, voter_ip),
        upvotes = r.upvotes + 1
    where r.id = request_id
    returning r.upvotes into upvotes;

    success := true;
    action := 'upvoted';
    return next;
end;
$$;

create or replace function public.toggle_upvote_dataset(request_id uuid, voter_ip text)
returns table(success boolean, upvotes integer, action text)
language plpgsql
security definer
as $$
declare
    current_ips text[];
    current_upvotes integer;
begin
    select r.voted_ips, r.upvotes
    into current_ips, current_upvotes
    from public.dataset_requests r
    where r.id = request_id
    for update;

    if not found then
        success := false;
        upvotes := 0;
        action := null;
        return next;
        return;
    end if;

    if voter_ip = any(current_ips) then
        update public.dataset_requests as r
        set
            voted_ips = array_remove(r.voted_ips, voter_ip),
            upvotes = greatest(r.upvotes - 1, 0)
        where r.id = request_id
        returning r.upvotes into upvotes;

        success := true;
        action := 'unvoted';
        return next;
        return;
    end if;

    update public.dataset_requests as r
    set
        voted_ips = array_append(r.voted_ips, voter_ip),
        upvotes = r.upvotes + 1
    where r.id = request_id
    returning r.upvotes into upvotes;

    success := true;
    action := 'upvoted';
    return next;
end;
$$;

alter table public.distillation_requests enable row level security;

alter table public.dataset_requests enable row level security;

alter table public.request_comments enable row level security;

drop policy if exists public_select on public.distillation_requests;

drop policy if exists public_insert on public.distillation_requests;

drop policy if exists public_update on public.distillation_requests;

drop policy if exists public_delete on public.distillation_requests;

drop policy if exists public_select on public.dataset_requests;

drop policy if exists public_insert on public.dataset_requests;

drop policy if exists public_update on public.dataset_requests;

drop policy if exists public_delete on public.dataset_requests;

drop policy if exists public_select on public.request_comments;

drop policy if exists public_insert on public.request_comments;

drop policy if exists public_update on public.request_comments;

drop policy if exists public_delete on public.request_comments;

create policy public_select on public.distillation_requests for
select using (true);

create policy public_insert on public.distillation_requests for insert
with
    check (true);

create policy public_select on public.dataset_requests for
select using (true);

create policy public_insert on public.dataset_requests for insert
with
    check (true);

create policy public_select on public.request_comments for
select using (true);

create policy public_insert on public.request_comments for insert
with
    check (role = 'user');

grant
select, insert on table public.distillation_requests to anon, authenticated;

grant
select, insert on table public.dataset_requests to anon, authenticated;

grant
select, insert on table public.request_comments to anon, authenticated;

grant
execute on function public.toggle_upvote_distillation (uuid, text) to anon,
authenticated;

grant
execute on function public.toggle_upvote_dataset (uuid, text) to anon,
authenticated;
