-- Migration: Add application-specific functions
-- This migration adds 9 functions that exist on the live database but were
-- not included in earlier schema migrations.

-- =============================================================================
-- 1. setup_initial_admin
-- Sets up an admin user by updating their user_type and username.
-- SECURITY DEFINER: Must be run as service_role or superuser.
-- =============================================================================
CREATE OR REPLACE FUNCTION public.setup_initial_admin(admin_email text)
RETURNS json
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
declare
    result json;
begin
    -- Only allow this to run as service role or superuser
    if not (
        current_user = 'service_role'
        or exists (
            select 1 from pg_roles
            where rolname = current_user
            and rolsuper
        )
    ) then
        raise exception 'Must be run as service_role or superuser';
    end if;

    -- Update user_type and username for admin
    update public.profiles
    set user_type = 'admin',
        username = coalesce(username, 'superadmin'),
        display_name = coalesce(display_name, 'Super Admin')
    where email = admin_email
    returning json_build_object(
        'id', id,
        'email', email,
        'user_type', user_type,
        'username', username,
        'display_name', display_name
    ) into result;

    if result is null then
        raise exception 'Admin user with email % not found', admin_email;
    end if;

    return result;
end;
$function$;

-- =============================================================================
-- 2. is_admin
-- Returns true if the current user has admin role.
-- =============================================================================
CREATE OR REPLACE FUNCTION public.is_admin()
RETURNS boolean
LANGUAGE sql
SECURITY DEFINER
AS $function$
  select coalesce(
    (select true
     from public.profiles
     where id = auth.uid()
     and user_type = 'admin'),
    false
  );
$function$;

-- =============================================================================
-- 3. is_super_admin
-- Alias for is_admin (same logic).
-- =============================================================================
CREATE OR REPLACE FUNCTION public.is_super_admin()
RETURNS boolean
LANGUAGE sql
SECURITY DEFINER
AS $function$
  select coalesce(
    (select true
     from public.profiles
     where id = auth.uid()
     and user_type = 'admin'),
    false
  );
$function$;

-- =============================================================================
-- 4. check_db_ready
-- Health check: verifies essential schemas, tables, and RLS are in place.
-- =============================================================================
CREATE OR REPLACE FUNCTION public.check_db_ready()
RETURNS boolean
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
begin
    -- Check if essential schemas exist
    if not exists (
        select 1
        from information_schema.schemata
        where schema_name in ('auth', 'storage', 'public')
    ) then
        return false;
    end if;

    -- Check if essential tables exist
    if not exists (
        select 1
        from information_schema.tables
        where table_schema = 'auth'
        and table_name = 'users'
    ) then
        return false;
    end if;

    -- Check if RLS is enabled on public.profiles
    if not exists (
        select 1
        from pg_tables
        where schemaname = 'public'
        and tablename = 'profiles'
        and rowsecurity = true
    ) then
        return false;
    end if;

    return true;
end;
$function$;

-- =============================================================================
-- 5. match_file_vectors
-- Vector similarity search over file artefacts.
-- NOTE: Requires the `file_vectors` table to exist (vector extension needed).
-- This function was created for a table that may not have been migrated yet.
-- =============================================================================
CREATE OR REPLACE FUNCTION public.match_file_vectors(
    filter jsonb,
    match_count integer,
    query_embedding vector
)
RETURNS TABLE(
    id bigint,
    file_id uuid,
    cabinet_id uuid,
    artefact_type text,
    artefact_is text,
    original_path_prefix text,
    original_filename text,
    content text,
    metadata jsonb,
    similarity double precision
)
LANGUAGE sql
STABLE
AS $function$
  select
    fv.id,
    nullif(fv.metadata->>'file_id','')::uuid              as file_id,
    nullif(fv.metadata->>'cabinet_id','')::uuid           as cabinet_id,
    nullif(fv.metadata->>'artefact_type','')              as artefact_type,
    nullif(fv.metadata->>'artefact_is','')                as artefact_is,
    nullif(fv.metadata->>'original_path_prefix','')       as original_path_prefix,
    nullif(fv.metadata->>'original_filename','')          as original_filename,
    fv.content,
    fv.metadata,
    1 - (fv.embedding <=> query_embedding)                as similarity
  from public.file_vectors fv
  where
    (coalesce(filter ? 'file_id', false)                = false or (fv.metadata->>'file_id')::uuid              = (filter->>'file_id')::uuid)
    and (coalesce(filter ? 'cabinet_id', false)         = false or (fv.metadata->>'cabinet_id')::uuid           = (filter->>'cabinet_id')::uuid)
    and (coalesce(filter ? 'artefact_type', false)      = false or (fv.metadata->>'artefact_type')              = (filter->>'artefact_type'))
    and (coalesce(filter ? 'artefact_id', false)        = false or (fv.metadata->>'artefact_id')                = (filter->>'artefact_id'))
    and (coalesce(filter ? 'original_path_prefix', false)     = false or (fv.metadata->>'original_path_prefix') like  (filter->>'original_path_prefix') || '%')
    and (coalesce(filter ? 'original_path_prefix_ilike', false)= false or (fv.metadata->>'original_path_prefix') ilike (filter->>'original_path_prefix_ilike') || '%')
    and (coalesce(filter ? 'original_filename', false)   = false or (fv.metadata->>'original_filename')         = (filter->>'original_filename'))
    and (coalesce(filter ? 'original_filename_ilike', false)= false or (fv.metadata->>'original_filename') ilike (filter->>'original_filename_ilike'))
  order by fv.embedding <=> query_embedding
  limit greatest(coalesce(match_count, 10), 1)
$function$;

-- =============================================================================
-- 6. set_completed_at
-- Trigger function: sets completed_at when status changes to 'completed'.
-- =============================================================================
CREATE OR REPLACE FUNCTION public.set_completed_at()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
begin
    if NEW.status = 'completed' and OLD.status != 'completed' then
        NEW.completed_at = now();
    end if;
    return NEW;
end;
$function$;

-- =============================================================================
-- 7. handle_updated_at
-- Trigger function: sets updated_at to current UTC time on UPDATE.
-- =============================================================================
CREATE OR REPLACE FUNCTION public.handle_updated_at()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
begin
    new.updated_at = timezone('utc'::text, now());
    return new;
end;
$function$;

-- =============================================================================
-- 8. update_updated_at_column
-- Alternative trigger function: sets updated_at to NOW() on UPDATE.
-- (Duplicate of handle_updated_at with slightly different syntax)
-- =============================================================================
CREATE OR REPLACE FUNCTION public.update_updated_at_column()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$function$;

-- =============================================================================
-- 9. exec_sql
-- Executes arbitrary SQL. SECURITY DEFINER — use with extreme caution.
-- =============================================================================
CREATE OR REPLACE FUNCTION public.exec_sql(query text)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
begin
    execute query;
end;
$function$;
