-- =====================================================================
--  GameDev Tracker — Schéma de base de données (Supabase / PostgreSQL)
--  À exécuter UNE SEULE FOIS dans : Supabase > SQL Editor > New query
-- =====================================================================

-- ---------- Types énumérés -------------------------------------------
do $$ begin
  create type member_role as enum
    ('lead','programmer','artist','game_designer','sound','writer','qa','other');
exception when duplicate_object then null; end $$;

do $$ begin
  create type task_status as enum ('todo','in_progress','review','done');
exception when duplicate_object then null; end $$;

do $$ begin
  create type priority_level as enum ('low','medium','high','urgent');
exception when duplicate_object then null; end $$;

do $$ begin
  create type bug_severity as enum ('low','medium','high','critical');
exception when duplicate_object then null; end $$;

do $$ begin
  create type bug_status as enum ('open','in_progress','resolved','closed');
exception when duplicate_object then null; end $$;

-- ---------- Profils (étend auth.users) -------------------------------
create table if not exists public.profiles (
  id           uuid primary key references auth.users(id) on delete cascade,
  full_name    text not null default 'Membre',
  role         member_role not null default 'other', -- rôle principal (= roles[0])
  roles        member_role[] not null default '{}',  -- tous les pôles du membre
  avatar_color text not null default '#7c5cff',
  avatar_url   text,                                 -- photo de profil (optionnelle)
  onboarded    boolean not null default false,       -- a terminé l'accueil
  created_at   timestamptz not null default now()
);

-- ---------- Projets --------------------------------------------------
create table if not exists public.projects (
  id          uuid primary key default gen_random_uuid(),
  name        text not null,
  description text,
  created_by  uuid references public.profiles(id) on delete set null,
  created_at  timestamptz not null default now()
);

-- ---------- Tâches ---------------------------------------------------
-- Un « pôle » (= member_role) reçoit la tâche ; une ou plusieurs
-- personnes de ce pôle peuvent y être assignées (assignee_ids).
create table if not exists public.tasks (
  id           uuid primary key default gen_random_uuid(),
  project_id   uuid not null references public.projects(id) on delete cascade,
  title        text not null,
  description  text,
  status       task_status not null default 'todo',
  priority     priority_level not null default 'medium',
  pole         member_role,
  assignee_ids uuid[] not null default '{}',
  due_date     date,
  position     double precision not null default 0,
  created_by   uuid references public.profiles(id) on delete set null,
  created_at   timestamptz not null default now(),
  updated_at   timestamptz not null default now()
);

-- ---------- Bugs -----------------------------------------------------
create table if not exists public.bugs (
  id          uuid primary key default gen_random_uuid(),
  project_id  uuid not null references public.projects(id) on delete cascade,
  title       text not null,
  description text,
  severity    bug_severity not null default 'medium',
  status      bug_status not null default 'open',
  priority    priority_level not null default 'medium',
  pole        member_role,
  assignee_id uuid references public.profiles(id) on delete set null,
  reporter_id uuid references public.profiles(id) on delete set null,
  created_at  timestamptz not null default now(),
  updated_at  timestamptz not null default now()
);

create index if not exists idx_tasks_project on public.tasks(project_id);
create index if not exists idx_bugs_project on public.bugs(project_id);

-- ---------- Maj automatique de updated_at ----------------------------
create or replace function public.touch_updated_at()
returns trigger language plpgsql as $$
begin new.updated_at = now(); return new; end $$;

drop trigger if exists trg_tasks_touch on public.tasks;
create trigger trg_tasks_touch before update on public.tasks
  for each row execute function public.touch_updated_at();

drop trigger if exists trg_bugs_touch on public.bugs;
create trigger trg_bugs_touch before update on public.bugs
  for each row execute function public.touch_updated_at();

-- ---------- Création auto du profil à l'inscription ------------------
create or replace function public.handle_new_user()
returns trigger language plpgsql security definer set search_path = public as $$
declare r member_role;
begin
  r := coalesce((new.raw_user_meta_data->>'role')::member_role, 'other');
  insert into public.profiles (id, full_name, role, roles)
  values (
    new.id,
    coalesce(new.raw_user_meta_data->>'full_name', split_part(new.email,'@',1)),
    r,
    array[r]
  )
  on conflict (id) do nothing;
  return new;
end $$;

drop trigger if exists on_auth_user_created on auth.users;
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute function public.handle_new_user();

-- ---------- Sécurité (RLS) -------------------------------------------
-- Équipe privée et de confiance : tout membre connecté a accès complet.
alter table public.profiles   enable row level security;
alter table public.projects   enable row level security;
alter table public.tasks      enable row level security;
alter table public.bugs       enable row level security;

do $$
declare t text;
begin
  foreach t in array array['profiles','projects','tasks','bugs'] loop
    execute format('drop policy if exists %I_all on public.%I', t, t);
    execute format(
      'create policy %I_all on public.%I for all to authenticated using (true) with check (true)',
      t, t);
  end loop;
end $$;

-- ---------- Realtime (synchro live entre membres) --------------------
do $$ begin
  alter publication supabase_realtime add table public.tasks;
exception when duplicate_object then null; end $$;
do $$ begin
  alter publication supabase_realtime add table public.bugs;
exception when duplicate_object then null; end $$;
do $$ begin
  alter publication supabase_realtime add table public.profiles;
exception when duplicate_object then null; end $$;

-- =====================================================================
--  ADMINISTRATION : comptes & rôles gérés par un panel admin in-app
-- =====================================================================

-- ---------- Champ "administrateur" sur les profils -------------------
alter table public.profiles
  add column if not exists is_admin boolean not null default false;

-- ---------- Sécurité fine sur les profils ----------------------------
-- On remplace la policy permissive "profiles_all" par des règles précises :
--   * tout le monde (connecté) PEUT LIRE les profils (affichage de l'équipe)
--   * chacun ne peut MODIFIER que SON propre profil
--   * la création/suppression de comptes passe uniquement par l'Edge
--     Function "admin-users" (clé service_role, côté serveur).
drop policy if exists profiles_all on public.profiles;
drop policy if exists profiles_select on public.profiles;
drop policy if exists profiles_update_self on public.profiles;

create policy profiles_select on public.profiles
  for select to authenticated using (true);

create policy profiles_update_self on public.profiles
  for update to authenticated
  using (id = auth.uid()) with check (id = auth.uid());

-- ---------- Garde-fou : on ne se promeut pas admin soi-même ----------
-- Empêche un membre connecté (rôle "authenticated") de changer le champ
-- is_admin. Les opérations légitimes passent par l'Edge Function
-- (rôle "service_role") ou par le SQL Editor (rôle "postgres"), tous deux
-- autorisés ici. Un admin déjà en place peut aussi modifier le statut.
create or replace function public.guard_is_admin()
returns trigger language plpgsql security definer set search_path = public as $$
begin
  if new.is_admin is distinct from old.is_admin
     and current_user = 'authenticated'
     and not exists (
       select 1 from public.profiles p where p.id = auth.uid() and p.is_admin
     ) then
    raise exception 'Seul un administrateur peut modifier le statut admin.';
  end if;
  return new;
end $$;

drop trigger if exists trg_profiles_guard_admin on public.profiles;
create trigger trg_profiles_guard_admin before update on public.profiles
  for each row execute function public.guard_is_admin();

-- ---------- Amorçage du PREMIER admin (à faire UNE fois) -------------
-- Après ta première inscription dans l'appli, exécute cette ligne en
-- remplaçant l'email par le tien pour devenir administrateur :
--
--   update public.profiles set is_admin = true
--   where id = (select id from auth.users where email = 'TON_EMAIL@ exemple.com');
--
-- Ensuite, tu pourras créer/gérer tous les autres comptes depuis l'appli.

-- =====================================================================
--  TAGS (« jobs ») & SUIVI DU TEMPS
-- =====================================================================

-- ---------- Tags sur les tâches (ex: Programmation, Art, Audio) ------
alter table public.tasks
  add column if not exists tags text[] not null default '{}';

-- ---------- Journal du temps passé (par tâche OU activité libre) -----
--  task_id null + label => temps « libre » (réunion, etc.).
create table if not exists public.time_logs (
  id         uuid primary key default gen_random_uuid(),
  project_id uuid references public.projects(id) on delete cascade,
  task_id    uuid references public.tasks(id) on delete cascade,
  label      text,
  user_id    uuid references public.profiles(id) on delete set null,
  minutes    integer not null check (minutes > 0),
  note       text,
  logged_at  date not null default current_date,
  created_at timestamptz not null default now()
);

create index if not exists idx_time_logs_task on public.time_logs(task_id);
create index if not exists idx_time_logs_project on public.time_logs(project_id);
create index if not exists idx_time_logs_user on public.time_logs(user_id);

alter table public.time_logs enable row level security;
drop policy if exists time_logs_all on public.time_logs;
create policy time_logs_all on public.time_logs
  for all to authenticated using (true) with check (true);

do $$ begin
  alter publication supabase_realtime add table public.time_logs;
exception when duplicate_object then null; end $$;

-- =====================================================================
--  NOTIFICATIONS (assignation d'une tâche / d'un bug à un pôle)
-- =====================================================================
create table if not exists public.notifications (
  id         uuid primary key default gen_random_uuid(),
  user_id    uuid not null references public.profiles(id) on delete cascade,
  project_id uuid references public.projects(id) on delete cascade,
  kind       text not null,                 -- 'task' | 'bug'
  title      text not null,                 -- ex : « Nouvelle tâche »
  body       text,                          -- titre de la tâche / du bug
  entity_id  uuid,                          -- id de la tâche / du bug
  pole       member_role,                   -- pôle destinataire
  read       boolean not null default false,
  created_at timestamptz not null default now()
);

create index if not exists idx_notifications_user on public.notifications(user_id);

alter table public.notifications enable row level security;

-- Chacun lit/modifie SES notifications ; l'insertion est ouverte à tout
-- membre connecté (afin de notifier les autres membres d'un pôle).
drop policy if exists notifications_select on public.notifications;
drop policy if exists notifications_insert on public.notifications;
drop policy if exists notifications_update on public.notifications;
drop policy if exists notifications_delete on public.notifications;

create policy notifications_select on public.notifications
  for select to authenticated using (user_id = auth.uid());
create policy notifications_insert on public.notifications
  for insert to authenticated with check (true);
create policy notifications_update on public.notifications
  for update to authenticated using (user_id = auth.uid()) with check (user_id = auth.uid());
create policy notifications_delete on public.notifications
  for delete to authenticated using (user_id = auth.uid());

do $$ begin
  alter publication supabase_realtime add table public.notifications;
exception when duplicate_object then null; end $$;

-- =====================================================================
--  STOCKAGE : photos de profil (bucket "avatars")
-- =====================================================================
insert into storage.buckets (id, name, public)
  values ('avatars', 'avatars', true)
  on conflict (id) do nothing;

drop policy if exists avatars_read   on storage.objects;
drop policy if exists avatars_insert on storage.objects;
drop policy if exists avatars_update on storage.objects;
drop policy if exists avatars_delete on storage.objects;

create policy avatars_read on storage.objects
  for select using (bucket_id = 'avatars');
create policy avatars_insert on storage.objects
  for insert to authenticated with check (bucket_id = 'avatars');
create policy avatars_update on storage.objects
  for update to authenticated using (bucket_id = 'avatars');
create policy avatars_delete on storage.objects
  for delete to authenticated using (bucket_id = 'avatars');
