-- ============================================================================
-- TABLERO CDC — MÉTRICAS PARA DASHBOARD FUTURO
-- Fecha: 2026-08-16
--
-- Objetivo:
--   1) Registrar usuarios/sesiones que realmente abren Tablero CDC.
--   2) Registrar eventos relevantes de proyectos sin depender del frontend.
--   3) Exponer un resumen listo para un futuro dashboard.
--
-- Es idempotente: puede ejecutarse nuevamente sin duplicar el backfill.
-- ============================================================================

create extension if not exists pgcrypto;

create table if not exists public.tablero_cdc_app_metrics_events (
  id uuid primary key default gen_random_uuid(),
  event_type text not null,
  event_key text not null unique,
  actor_user_id uuid references auth.users(id) on delete set null,
  actor_email text,
  actor_name text,
  project_id uuid references public.tablero_cdc_projects(id) on delete set null,
  session_id text,
  metadata jsonb not null default '{}'::jsonb,
  occurred_at timestamptz not null default now(),
  created_at timestamptz not null default now()
);

create index if not exists tablero_cdc_metrics_event_type_idx
  on public.tablero_cdc_app_metrics_events (event_type, occurred_at desc);

create index if not exists tablero_cdc_metrics_actor_idx
  on public.tablero_cdc_app_metrics_events (actor_user_id, occurred_at desc);

create index if not exists tablero_cdc_metrics_project_idx
  on public.tablero_cdc_app_metrics_events (project_id, occurred_at desc)
  where project_id is not null;

alter table public.tablero_cdc_app_metrics_events enable row level security;

-- El dashboard futuro podrá leer las métricas únicamente con usuarios activos
-- que ya tienen permiso para ver/controlar el resumen económico del Tablero.
drop policy if exists "tablero_cdc_metrics_select_authorized" on public.tablero_cdc_app_metrics_events;
create policy "tablero_cdc_metrics_select_authorized"
on public.tablero_cdc_app_metrics_events
for select
to authenticated
using (
  exists (
    select 1
    from public.tablero_cdc_allowed_users u
    where lower(u.email) = lower(coalesce(auth.jwt() ->> 'email', ''))
      and u.is_active = true
      and coalesce(u.can_control_pricing_summary, false) = true
  )
);

-- No damos INSERT directo al cliente. Las aperturas se registran vía RPC y
-- los eventos de proyecto vía trigger, evitando que el navegador falsifique métricas.
revoke insert, update, delete on public.tablero_cdc_app_metrics_events from authenticated;

grant select on public.tablero_cdc_app_metrics_events to authenticated;

-- --------------------------------------------------------------------------
-- Helper de identidad para los eventos automáticos.
-- --------------------------------------------------------------------------
create or replace function public.tablero_cdc_metric_actor(
  p_user_id uuid,
  out actor_email text,
  out actor_name text
)
language plpgsql
security definer
set search_path = public, auth
as $$
begin
  actor_email := null;
  actor_name := null;

  if p_user_id is null then
    return;
  end if;

  select
    u.email,
    coalesce(
      nullif(u.raw_user_meta_data ->> 'full_name', ''),
      nullif(u.raw_user_meta_data ->> 'name', ''),
      u.email
    )
  into actor_email, actor_name
  from auth.users u
  where u.id = p_user_id
  limit 1;
end;
$$;

revoke all on function public.tablero_cdc_metric_actor(uuid) from public;

-- --------------------------------------------------------------------------
-- RPC: una apertura por sesión autenticada/autorizada.
-- --------------------------------------------------------------------------
create or replace function public.tablero_cdc_track_app_open(p_session_id text)
returns void
language plpgsql
security definer
set search_path = public, auth
as $$
declare
  v_uid uuid := auth.uid();
  v_email text;
  v_name text;
  v_session text := nullif(trim(coalesce(p_session_id, '')), '');
begin
  if v_uid is null then
    raise exception 'Usuario no autenticado';
  end if;

  if v_session is null or length(v_session) > 200 then
    raise exception 'session_id inválido';
  end if;

  select actor_email, actor_name
  into v_email, v_name
  from public.tablero_cdc_metric_actor(v_uid);

  if not exists (
    select 1
    from public.tablero_cdc_allowed_users u
    where lower(u.email) = lower(coalesce(v_email, ''))
      and u.is_active = true
  ) then
    raise exception 'Usuario no autorizado para Tablero CDC';
  end if;

  insert into public.tablero_cdc_app_metrics_events (
    event_type,
    event_key,
    actor_user_id,
    actor_email,
    actor_name,
    session_id,
    metadata
  ) values (
    'app_opened',
    'app_opened:' || v_uid::text || ':' || v_session,
    v_uid,
    v_email,
    v_name,
    v_session,
    jsonb_build_object('app', 'tablero_cdc')
  )
  on conflict (event_key) do nothing;
end;
$$;

revoke all on function public.tablero_cdc_track_app_open(text) from public;
grant execute on function public.tablero_cdc_track_app_open(text) to authenticated;

-- --------------------------------------------------------------------------
-- Trigger de proyectos: creación + cambios de estado relevantes.
-- --------------------------------------------------------------------------
create or replace function public.tablero_cdc_capture_project_metric()
returns trigger
language plpgsql
security definer
set search_path = public, auth
as $$
declare
  v_actor uuid;
  v_email text;
  v_name text;
  v_old_status text;
  v_new_status text;
  v_event text;
begin
  v_actor := coalesce(new.updated_by, new.created_by, auth.uid());

  select actor_email, actor_name
  into v_email, v_name
  from public.tablero_cdc_metric_actor(v_actor);

  if tg_op = 'INSERT' then
    insert into public.tablero_cdc_app_metrics_events (
      event_type, event_key, actor_user_id, actor_email, actor_name,
      project_id, metadata, occurred_at
    ) values (
      'project_created',
      'project_created:' || new.id::text,
      v_actor,
      v_email,
      v_name,
      new.id,
      jsonb_build_object(
        'title', coalesce(new.title, ''),
        'client', coalesce(new.client, ''),
        'brand', coalesce(new.brand, ''),
        'country', coalesce(new.country, '')
      ),
      coalesce(new.created_at, now())
    )
    on conflict (event_key) do nothing;

    return new;
  end if;

  v_old_status := lower(trim(coalesce(old.status, '')));
  v_new_status := lower(trim(coalesce(new.status, '')));

  if v_old_status is not distinct from v_new_status then
    return new;
  end if;

  v_event := case
    when v_new_status = 'aprobado' then 'project_approved'
    when v_new_status = 'no aprobado' then 'project_rejected'
    when v_new_status in ('', 'activo') then 'project_reopened'
    else 'project_closed'
  end;

  insert into public.tablero_cdc_app_metrics_events (
    event_type, event_key, actor_user_id, actor_email, actor_name,
    project_id, metadata
  ) values (
    v_event,
    v_event || ':' || new.id::text || ':' || gen_random_uuid()::text,
    v_actor,
    v_email,
    v_name,
    new.id,
    jsonb_build_object(
      'title', coalesce(new.title, ''),
      'previous_status', coalesce(old.status, ''),
      'new_status', coalesce(new.status, '')
    )
  );

  return new;
end;
$$;

drop trigger if exists trg_tablero_cdc_project_metrics on public.tablero_cdc_projects;
create trigger trg_tablero_cdc_project_metrics
after insert or update of status
on public.tablero_cdc_projects
for each row
execute function public.tablero_cdc_capture_project_metric();

-- --------------------------------------------------------------------------
-- Trigger de tiempo: permite medir uso real y horas registradas históricamente.
-- --------------------------------------------------------------------------
create or replace function public.tablero_cdc_capture_time_metric()
returns trigger
language plpgsql
security definer
set search_path = public, auth
as $$
begin
  insert into public.tablero_cdc_app_metrics_events (
    event_type, event_key, actor_user_id, actor_email, actor_name,
    project_id, metadata, occurred_at
  ) values (
    'time_logged',
    'time_logged:' || new.id::text,
    new.created_by,
    new.created_by_email,
    coalesce(nullif(new.created_by_name, ''), new.created_by_email),
    new.project_id,
    jsonb_build_object(
      'country', coalesce(new.country, ''),
      'task_name', coalesce(new.task_name, ''),
      'duration_minutes', coalesce(new.duration_minutes, 0),
      'work_date', new.work_date
    ),
    coalesce(new.created_at, now())
  )
  on conflict (event_key) do nothing;

  return new;
end;
$$;

drop trigger if exists trg_tablero_cdc_time_metrics on public.tablero_cdc_project_time_entries;
create trigger trg_tablero_cdc_time_metrics
after insert
on public.tablero_cdc_project_time_entries
for each row
execute function public.tablero_cdc_capture_time_metric();

-- --------------------------------------------------------------------------
-- Métricas de Briefs (siempre útiles para este Tablero): recibidos/aprobados/
-- rechazados. La tabla ya forma parte del módulo CDC Brief de esta versión.
-- --------------------------------------------------------------------------
create or replace function public.tablero_cdc_capture_brief_metric()
returns trigger
language plpgsql
security definer
set search_path = public, auth
as $$
declare
  v_actor uuid;
  v_email text;
  v_name text;
  v_event text;
begin
  if tg_op = 'INSERT' then
    insert into public.tablero_cdc_app_metrics_events (
      event_type, event_key, actor_user_id, actor_email, actor_name, metadata, occurred_at
    ) values (
      'brief_received',
      'brief_received:' || new.id::text,
      null,
      null,
      null,
      jsonb_build_object('brief_id', coalesce(new.brief_id, ''), 'title', coalesce(new.title, '')),
      coalesce(new.created_at, now())
    )
    on conflict (event_key) do nothing;
    return new;
  end if;

  if old.review_status is not distinct from new.review_status then
    return new;
  end if;

  v_event := case
    when new.review_status = 'approved' then 'brief_approved'
    when new.review_status = 'rejected' then 'brief_rejected'
    else null
  end;

  if v_event is null then
    return new;
  end if;

  v_actor := coalesce(new.reviewed_by, auth.uid());
  select actor_email, actor_name
  into v_email, v_name
  from public.tablero_cdc_metric_actor(v_actor);

  insert into public.tablero_cdc_app_metrics_events (
    event_type, event_key, actor_user_id, actor_email, actor_name,
    project_id, metadata
  ) values (
    v_event,
    v_event || ':' || new.id::text || ':' || gen_random_uuid()::text,
    v_actor,
    coalesce(new.reviewed_by_email, v_email),
    coalesce(new.reviewed_by_name, v_name),
    new.approved_project_id,
    jsonb_build_object(
      'brief_id', coalesce(new.brief_id, ''),
      'title', coalesce(new.title, ''),
      'review_status', new.review_status
    )
  );

  return new;
end;
$$;

drop trigger if exists trg_tablero_cdc_brief_metrics on public.tablero_cdc_brief_inbox;
create trigger trg_tablero_cdc_brief_metrics
after insert or update of review_status
on public.tablero_cdc_brief_inbox
for each row
execute function public.tablero_cdc_capture_brief_metric();

-- --------------------------------------------------------------------------
-- Backfill seguro: proyectos y briefs existentes para que el futuro dashboard
-- no empiece desde cero. No duplica si se ejecuta otra vez.
-- --------------------------------------------------------------------------
insert into public.tablero_cdc_app_metrics_events (
  event_type, event_key, actor_user_id, actor_email, actor_name,
  project_id, metadata, occurred_at
)
select
  'project_created',
  'project_created:' || p.id::text,
  p.created_by,
  au.email,
  coalesce(nullif(au.raw_user_meta_data ->> 'full_name', ''), nullif(au.raw_user_meta_data ->> 'name', ''), au.email),
  p.id,
  jsonb_build_object(
    'title', coalesce(p.title, ''),
    'client', coalesce(p.client, ''),
    'brand', coalesce(p.brand, ''),
    'country', coalesce(p.country, ''),
    'backfill', true
  ),
  coalesce(p.created_at, now())
from public.tablero_cdc_projects p
left join auth.users au on au.id = p.created_by
on conflict (event_key) do nothing;

insert into public.tablero_cdc_app_metrics_events (
  event_type, event_key, actor_user_id, actor_email, actor_name,
  project_id, metadata, occurred_at
)
select
  case when lower(trim(p.status)) = 'aprobado' then 'project_approved' else 'project_rejected' end,
  case when lower(trim(p.status)) = 'aprobado'
       then 'project_approved:backfill:' || p.id::text
       else 'project_rejected:backfill:' || p.id::text end,
  p.updated_by,
  au.email,
  coalesce(nullif(au.raw_user_meta_data ->> 'full_name', ''), nullif(au.raw_user_meta_data ->> 'name', ''), au.email),
  p.id,
  jsonb_build_object('status', p.status, 'backfill', true),
  now()
from public.tablero_cdc_projects p
left join auth.users au on au.id = p.updated_by
where lower(trim(coalesce(p.status, ''))) in ('aprobado', 'no aprobado')
on conflict (event_key) do nothing;

insert into public.tablero_cdc_app_metrics_events (
  event_type, event_key, actor_user_id, actor_email, actor_name,
  project_id, metadata, occurred_at
)
select
  'time_logged',
  'time_logged:' || t.id::text,
  t.created_by,
  t.created_by_email,
  coalesce(nullif(t.created_by_name, ''), t.created_by_email),
  t.project_id,
  jsonb_build_object(
    'country', coalesce(t.country, ''),
    'task_name', coalesce(t.task_name, ''),
    'duration_minutes', coalesce(t.duration_minutes, 0),
    'work_date', t.work_date,
    'backfill', true
  ),
  coalesce(t.created_at, now())
from public.tablero_cdc_project_time_entries t
on conflict (event_key) do nothing;

insert into public.tablero_cdc_app_metrics_events (
  event_type, event_key, actor_user_id, actor_email, actor_name,
  project_id, metadata, occurred_at
)
select
  'brief_received',
  'brief_received:' || b.id::text,
  null,
  null,
  null,
  b.approved_project_id,
  jsonb_build_object('brief_id', coalesce(b.brief_id, ''), 'title', coalesce(b.title, ''), 'backfill', true),
  coalesce(b.created_at, now())
from public.tablero_cdc_brief_inbox b
on conflict (event_key) do nothing;

-- --------------------------------------------------------------------------
-- Vista resumen para el dashboard futuro.
-- Los conteos de estado salen del estado ACTUAL de proyectos, evitando inflar
-- métricas cuando un proyecto cambia de estado varias veces.
-- --------------------------------------------------------------------------
create or replace view public.tablero_cdc_dashboard_metrics as
select
  (select count(distinct actor_user_id)
     from public.tablero_cdc_app_metrics_events
    where actor_user_id is not null) as usuarios_que_han_usado_app,

  (select count(*)
     from public.tablero_cdc_app_metrics_events
    where event_type = 'app_opened') as sesiones_registradas,

  (select count(*)
     from public.tablero_cdc_app_metrics_events
    where event_type = 'project_created') as proyectos_creados_historico,

  (select count(*) from public.tablero_cdc_projects) as proyectos_actuales,

  (select count(*)
     from public.tablero_cdc_projects
    where lower(trim(coalesce(status, ''))) in ('', 'activo')) as proyectos_activos_actuales,

  (select count(*)
     from public.tablero_cdc_projects
    where lower(trim(coalesce(status, ''))) = 'aprobado') as proyectos_aprobados_actuales,

  (select count(*)
     from public.tablero_cdc_projects
    where lower(trim(coalesce(status, ''))) = 'no aprobado') as proyectos_rechazados_actuales,

  (select count(*)
     from public.tablero_cdc_app_metrics_events
    where event_type = 'project_approved') as aprobaciones_registradas,

  (select count(*)
     from public.tablero_cdc_app_metrics_events
    where event_type = 'project_rejected') as rechazos_registrados,

  (select count(*)
     from public.tablero_cdc_project_time_entries) as registros_de_tiempo,

  (select round(coalesce(sum(duration_minutes), 0)::numeric / 60.0, 2)
     from public.tablero_cdc_project_time_entries) as horas_registradas,

  (select count(*) from public.tablero_cdc_brief_inbox) as briefs_recibidos,

  (select count(*)
     from public.tablero_cdc_brief_inbox
    where review_status = 'approved') as briefs_aprobados,

  (select count(*)
     from public.tablero_cdc_brief_inbox
    where review_status = 'rejected') as briefs_rechazados,

  now() as consultado_en;

grant select on public.tablero_cdc_dashboard_metrics to authenticated;

comment on table public.tablero_cdc_app_metrics_events is
  'Eventos de uso y negocio de Tablero CDC para dashboard futuro de aplicaciones GLM.';

comment on view public.tablero_cdc_dashboard_metrics is
  'Resumen actual de usuarios, proyectos, tiempos y briefs de Tablero CDC.';
