-- Training plans and sessions for analysis-driven coaching
create table if not exists public.training_plans (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references auth.users(id) on delete cascade,
  pat_type text not null,
  status text not null default 'active' check (status in ('active', 'archived')),
  analysis_snapshot jsonb not null default '{}'::jsonb,
  duration_weeks integer not null check (duration_weeks in (2, 4, 6)),
  sessions_per_week integer not null check (sessions_per_week in (2, 3, 4)),
  generated_at timestamptz not null default now(),
  archived_at timestamptz,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create table if not exists public.training_plan_sessions (
  id uuid primary key default gen_random_uuid(),
  plan_id uuid not null references public.training_plans(id) on delete cascade,
  week_no integer not null,
  session_no integer not null,
  main_exercise text not null,
  secondary_exercises jsonb not null default '[]'::jsonb,
  tasks jsonb not null default '[]'::jsonb,
  state text not null default 'open' check (state in ('open', 'done', 'skipped')),
  notes text,
  completed_at timestamptz,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique (plan_id, week_no, session_no)
);

-- updated_at trigger reuse
create or replace function public.set_updated_at()
returns trigger as $$
begin
  new.updated_at = now();
  return new;
end;
$$ language plpgsql;

drop trigger if exists trg_training_plans_updated_at on public.training_plans;
create trigger trg_training_plans_updated_at
before update on public.training_plans
for each row execute function public.set_updated_at();

drop trigger if exists trg_training_plan_sessions_updated_at on public.training_plan_sessions;
create trigger trg_training_plan_sessions_updated_at
before update on public.training_plan_sessions
for each row execute function public.set_updated_at();

-- RLS
alter table public.training_plans enable row level security;
alter table public.training_plan_sessions enable row level security;

drop policy if exists "Allow users to insert own training plans" on public.training_plans;
create policy "Allow users to insert own training plans"
on public.training_plans
for insert
with check (auth.uid() = user_id);

drop policy if exists "Allow users to select own training plans" on public.training_plans;
create policy "Allow users to select own training plans"
on public.training_plans
for select
using (auth.uid() = user_id);

drop policy if exists "Allow users to update own training plans" on public.training_plans;
create policy "Allow users to update own training plans"
on public.training_plans
for update
using (auth.uid() = user_id);

drop policy if exists "Allow users to delete own training plans" on public.training_plans;
create policy "Allow users to delete own training plans"
on public.training_plans
for delete
using (auth.uid() = user_id);

drop policy if exists "Allow users to insert own training plan sessions" on public.training_plan_sessions;
create policy "Allow users to insert own training plan sessions"
on public.training_plan_sessions
for insert
with check (
  exists (
    select 1
    from public.training_plans tp
    where tp.id = training_plan_sessions.plan_id
      and tp.user_id = auth.uid()
  )
);

drop policy if exists "Allow users to select own training plan sessions" on public.training_plan_sessions;
create policy "Allow users to select own training plan sessions"
on public.training_plan_sessions
for select
using (
  exists (
    select 1
    from public.training_plans tp
    where tp.id = training_plan_sessions.plan_id
      and tp.user_id = auth.uid()
  )
);

drop policy if exists "Allow users to update own training plan sessions" on public.training_plan_sessions;
create policy "Allow users to update own training plan sessions"
on public.training_plan_sessions
for update
using (
  exists (
    select 1
    from public.training_plans tp
    where tp.id = training_plan_sessions.plan_id
      and tp.user_id = auth.uid()
  )
);

drop policy if exists "Allow users to delete own training plan sessions" on public.training_plan_sessions;
create policy "Allow users to delete own training plan sessions"
on public.training_plan_sessions
for delete
using (
  exists (
    select 1
    from public.training_plans tp
    where tp.id = training_plan_sessions.plan_id
      and tp.user_id = auth.uid()
  )
);

create index if not exists idx_training_plans_user_pat_generated
on public.training_plans(user_id, pat_type, generated_at desc);

create unique index if not exists idx_training_plans_user_pat_active_unique
on public.training_plans(user_id, pat_type)
where status = 'active';

create index if not exists idx_training_plan_sessions_plan_week_session
on public.training_plan_sessions(plan_id, week_no, session_no);

create or replace function public.create_training_plan_with_sessions(
  p_pat_type text,
  p_analysis_snapshot jsonb,
  p_duration_weeks integer,
  p_sessions_per_week integer,
  p_sessions jsonb
)
returns uuid
language plpgsql
security invoker
as $$
declare
  v_user_id uuid;
  v_plan_id uuid;
  v_session jsonb;
  v_week_no integer;
  v_session_no integer;
begin
  v_user_id := auth.uid();

  if v_user_id is null then
    raise exception 'Not authenticated';
  end if;

  if p_duration_weeks not in (2, 4, 6) then
    raise exception 'Invalid duration_weeks: %', p_duration_weeks;
  end if;

  if p_sessions_per_week not in (2, 3, 4) then
    raise exception 'Invalid sessions_per_week: %', p_sessions_per_week;
  end if;

  update public.training_plans
  set status = 'archived', archived_at = now()
  where user_id = v_user_id
    and pat_type = p_pat_type
    and status = 'active';

  insert into public.training_plans (
    user_id,
    pat_type,
    status,
    analysis_snapshot,
    duration_weeks,
    sessions_per_week,
    generated_at
  )
  values (
    v_user_id,
    p_pat_type,
    'active',
    coalesce(p_analysis_snapshot, '{}'::jsonb),
    p_duration_weeks,
    p_sessions_per_week,
    now()
  )
  returning id into v_plan_id;

  for v_session in
    select value
    from jsonb_array_elements(coalesce(p_sessions, '[]'::jsonb))
  loop
    v_week_no := coalesce((v_session->>'weekNo')::integer, (v_session->>'week_no')::integer, 1);
    v_session_no := coalesce((v_session->>'sessionNo')::integer, (v_session->>'session_no')::integer, 1);

    insert into public.training_plan_sessions (
      plan_id,
      week_no,
      session_no,
      main_exercise,
      secondary_exercises,
      tasks,
      state,
      notes
    )
    values (
      v_plan_id,
      v_week_no,
      v_session_no,
      coalesce(v_session->>'mainExercise', v_session->>'main_exercise', 'Basistechnik'),
      coalesce(v_session->'secondaryExercises', v_session->'secondary_exercises', '[]'::jsonb),
      coalesce(v_session->'tasks', '[]'::jsonb),
      coalesce(v_session->>'state', 'open'),
      nullif(v_session->>'notes', '')
    );
  end loop;

  return v_plan_id;
end;
$$;

grant execute on function public.create_training_plan_with_sessions(text, jsonb, integer, integer, jsonb)
to authenticated;
