create or replace function public.get_my_role()
returns text as $$
  select role from public.profiles where id = auth.uid();
$$ language sql security definer stable
set search_path = public;

create or replace function public.is_external()
returns boolean as $$
  select get_my_role() = 'external';
$$ language sql security definer stable
set search_path = public;

create or replace function public.get_my_company_id()
returns uuid as $$
  select company_id from public.profiles where id = auth.uid();
$$ language sql security definer stable
set search_path = public;

create or replace function public.has_company_access(company uuid)
returns boolean as $$
  select exists (
    select 1
    from public.profiles p
    where p.id = auth.uid()
      and (
        p.company_id = company
        or exists (
          select 1
          from public.company_members cm
          where cm.profile_id = auth.uid()
            and cm.company_id = company
        )
      )
  );
$$ language sql security definer stable
set search_path = public;

create or replace function public.guard_task_update()
returns trigger as $$
declare
  caller_role text;
begin
  select role into caller_role from public.profiles where id = auth.uid();

  if caller_role = 'client' then
    new.project_id := old.project_id;
    new.invoiced := old.invoiced;

    if not (
      new.status = 'not_started'
      and coalesce(new.current_version, 0) > coalesce(old.current_version, 0)
      and new.assigned_to is null
      and new.assigned_name is null
    ) then
      new.assigned_to := old.assigned_to;
      new.assigned_name := old.assigned_name;
    end if;
  elsif caller_role = 'external' then
    new.project_id := old.project_id;
    new.invoiced := old.invoiced;
  end if;

  return new;
end;
$$ language plpgsql security definer
set search_path = public;

create or replace function public.handle_new_user()
returns trigger as $$
begin
  insert into public.profiles (id, name, email, role)
  values (
    new.id,
    coalesce(new.raw_user_meta_data->>'name', ''),
    new.email,
    coalesce(new.raw_user_meta_data->>'role', 'client')
  );
  return new;
end;
$$ language plpgsql security definer
set search_path = public;

create or replace function public.prevent_duplicate_active_subcontractor_po_task()
returns trigger as $$
declare
  current_po_status text;
  duplicate_po_number text;
begin
  if new.task_id is null then
    return new;
  end if;

  select status into current_po_status
  from public.subcontractor_payments
  where id = new.po_id;

  if current_po_status = 'cancelled' then
    return new;
  end if;

  select sp.po_number into duplicate_po_number
  from public.subcontractor_po_items item
  join public.subcontractor_payments sp on sp.id = item.po_id
  where item.task_id = new.task_id
    and item.id <> coalesce(new.id, '00000000-0000-0000-0000-000000000000'::uuid)
    and sp.status <> 'cancelled'
  limit 1;

  if duplicate_po_number is not null then
    raise exception 'Task is already attached to active subcontractor PO %', duplicate_po_number
      using errcode = '23505';
  end if;

  return new;
end;
$$ language plpgsql security definer
set search_path = public;

create or replace function public.prevent_duplicate_on_subcontractor_po_reactivation()
returns trigger as $$
declare
  duplicate_task_title text;
begin
  if old.status = new.status or new.status = 'cancelled' then
    return new;
  end if;

  select t.title into duplicate_task_title
  from public.subcontractor_po_items current_item
  join public.subcontractor_po_items other_item
    on other_item.task_id = current_item.task_id
   and other_item.po_id <> current_item.po_id
  join public.subcontractor_payments other_po
    on other_po.id = other_item.po_id
   and other_po.status <> 'cancelled'
  left join public.tasks t on t.id = current_item.task_id
  where current_item.po_id = new.id
    and current_item.task_id is not null
  limit 1;

  if duplicate_task_title is not null then
    raise exception 'Cannot reactivate PO because task "%" is already attached to another active subcontractor PO', duplicate_task_title
      using errcode = '23505';
  end if;

  return new;
end;
$$ language plpgsql security definer
set search_path = public;

create or replace function public.sync_subcontractor_project_member()
returns trigger as $$
begin
  if new.profile_id is not null and new.project_id is not null then
    insert into public.project_members (project_id, profile_id)
    values (new.project_id, new.profile_id)
    on conflict (project_id, profile_id) do nothing;
  end if;
  return new;
end;
$$ language plpgsql security definer
set search_path = public;

create or replace function public.sync_project_member_on_task_assign()
returns trigger as $$
begin
  if new.assigned_to is not null and new.project_id is not null then
    insert into public.project_members (project_id, profile_id)
    values (new.project_id, new.assigned_to)
    on conflict (project_id, profile_id) do nothing;
  end if;
  return new;
end;
$$ language plpgsql security definer
set search_path = public;

do $$
declare
  fn record;
begin
  for fn in
    select n.nspname as schema_name, p.proname as function_name, pg_get_function_identity_arguments(p.oid) as identity_args
    from pg_proc p
    join pg_namespace n on n.oid = p.pronamespace
    where n.nspname = 'public'
      and p.proname in (
        'notify_company_folder_sync',
        'notify_profile_folder_sync',
        'sync_project_status'
      )
  loop
    execute format(
      'drop function if exists %I.%I(%s) cascade;',
      fn.schema_name,
      fn.function_name,
      fn.identity_args
    );
  end loop;
end
$$;
