-- ═══════════════════════════════════════════════════════════════════════════
-- Support Sessions — Sessões de suporte técnico SaaS
-- ═══════════════════════════════════════════════════════════════════════════
-- Permite que admins SaaS gerem tokens de acesso temporário para debug
-- de agendas de terapeutas, sem expor debug para usuários comuns.
--
-- SEGURANÇA:
--   - RLS: só saas_admin pode criar/listar sessões
--   - Token é opaco (gen_random_uuid) — não adivinhável
--   - expires_at com TTL máximo de 60 minutos
--   - validate_support_session() retorna apenas true/false + tenant_id
--     (não expõe dados do admin)
-- ═══════════════════════════════════════════════════════════════════════════

-- ── Tabela ──────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS "public"."support_sessions" (
  "id"          uuid    DEFAULT gen_random_uuid() NOT NULL,
  "tenant_id"   uuid    NOT NULL,
  "admin_id"    uuid    NOT NULL,
  "token"       text    NOT NULL DEFAULT (replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', '')),
  "expires_at"  timestamp with time zone NOT NULL
                  DEFAULT (now() + interval '60 minutes'),
  "created_at"  timestamp with time zone NOT NULL DEFAULT now(),

  CONSTRAINT "support_sessions_pkey" PRIMARY KEY ("id"),

  CONSTRAINT "support_sessions_tenant_fk"
    FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE CASCADE,

  CONSTRAINT "support_sessions_admin_fk"
    FOREIGN KEY ("admin_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE,

  CONSTRAINT "support_sessions_token_unique" UNIQUE ("token")
);

-- ── Índices ──────────────────────────────────────────────────────────────────

CREATE INDEX IF NOT EXISTS "support_sessions_token_idx"
  ON "public"."support_sessions" ("token");

CREATE INDEX IF NOT EXISTS "support_sessions_tenant_idx"
  ON "public"."support_sessions" ("tenant_id");

CREATE INDEX IF NOT EXISTS "support_sessions_expires_idx"
  ON "public"."support_sessions" ("expires_at");

-- ── RLS ──────────────────────────────────────────────────────────────────────

ALTER TABLE "public"."support_sessions" ENABLE ROW LEVEL SECURITY;

-- Somente saas_admin pode ver suas próprias sessões de suporte
DROP POLICY IF EXISTS "support_sessions_saas_select" ON "public"."support_sessions";
CREATE POLICY "support_sessions_saas_select"
  ON "public"."support_sessions"
  FOR SELECT
  USING (
    auth.uid() = admin_id
    AND EXISTS (
      SELECT 1 FROM public.profiles
      WHERE id = auth.uid()
        AND role = 'saas_admin'
    )
  );

-- Somente saas_admin pode criar sessões de suporte
DROP POLICY IF EXISTS "support_sessions_saas_insert" ON "public"."support_sessions";
CREATE POLICY "support_sessions_saas_insert"
  ON "public"."support_sessions"
  FOR INSERT
  WITH CHECK (
    auth.uid() = admin_id
    AND EXISTS (
      SELECT 1 FROM public.profiles
      WHERE id = auth.uid()
        AND role = 'saas_admin'
    )
  );

-- Somente saas_admin pode deletar suas próprias sessões
DROP POLICY IF EXISTS "support_sessions_saas_delete" ON "public"."support_sessions";
CREATE POLICY "support_sessions_saas_delete"
  ON "public"."support_sessions"
  FOR DELETE
  USING (
    auth.uid() = admin_id
    AND EXISTS (
      SELECT 1 FROM public.profiles
      WHERE id = auth.uid()
        AND role = 'saas_admin'
    )
  );

-- ── RPC: create_support_session ───────────────────────────────────────────────
-- Cria uma sessão de suporte para um tenant.
-- Apenas saas_admin pode chamar. TTL: 60 minutos (configurável via p_ttl_minutes).
-- Retorna: token, expires_at

DROP FUNCTION IF EXISTS public.create_support_session(uuid, integer);
CREATE OR REPLACE FUNCTION public.create_support_session(
  p_tenant_id     uuid,
  p_ttl_minutes   integer DEFAULT 60
)
RETURNS json
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  v_admin_id  uuid;
  v_role      text;
  v_token     text;
  v_expires   timestamp with time zone;
  v_session   support_sessions;
BEGIN
  -- Verifica autenticação
  v_admin_id := auth.uid();
  IF v_admin_id IS NULL THEN
    RAISE EXCEPTION 'Não autenticado.' USING ERRCODE = 'P0001';
  END IF;

  -- Verifica role saas_admin
  SELECT role INTO v_role
    FROM public.profiles
   WHERE id = v_admin_id;

  IF v_role <> 'saas_admin' THEN
    RAISE EXCEPTION 'Acesso negado. Somente saas_admin pode criar sessões de suporte.'
      USING ERRCODE = 'P0002';
  END IF;

  -- Valida TTL (1 a 120 minutos)
  IF p_ttl_minutes < 1 OR p_ttl_minutes > 120 THEN
    RAISE EXCEPTION 'TTL inválido. Use entre 1 e 120 minutos.'
      USING ERRCODE = 'P0003';
  END IF;

  -- Valida tenant
  IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = p_tenant_id) THEN
    RAISE EXCEPTION 'Tenant não encontrado.'
      USING ERRCODE = 'P0004';
  END IF;

  -- Gera token único (64 chars hex, sem pgcrypto)
  v_token   := replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', '');
  v_expires := now() + (p_ttl_minutes || ' minutes')::interval;

  -- Insere sessão
  INSERT INTO public.support_sessions (tenant_id, admin_id, token, expires_at)
  VALUES (p_tenant_id, v_admin_id, v_token, v_expires)
  RETURNING * INTO v_session;

  RETURN json_build_object(
    'token',      v_session.token,
    'expires_at', v_session.expires_at,
    'session_id', v_session.id
  );
END;
$$;

-- ── RPC: validate_support_session ────────────────────────────────────────────
-- Valida um token de suporte. Não requer autenticação (chamada pública).
-- Retorna: { valid: bool, tenant_id: uuid|null }
-- NUNCA retorna admin_id ou dados internos.

DROP FUNCTION IF EXISTS public.validate_support_session(text);
CREATE OR REPLACE FUNCTION public.validate_support_session(
  p_token text
)
RETURNS json
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  v_session support_sessions;
BEGIN
  IF p_token IS NULL OR length(trim(p_token)) < 32 THEN
    RETURN json_build_object('valid', false, 'tenant_id', null);
  END IF;

  SELECT * INTO v_session
    FROM public.support_sessions
   WHERE token = p_token
     AND expires_at > now()
   LIMIT 1;

  IF NOT FOUND THEN
    RETURN json_build_object('valid', false, 'tenant_id', null);
  END IF;

  RETURN json_build_object(
    'valid',     true,
    'tenant_id', v_session.tenant_id
  );
END;
$$;

-- ── RPC: revoke_support_session ───────────────────────────────────────────────
-- Revoga um token manualmente. Apenas o admin que criou pode revogar.

DROP FUNCTION IF EXISTS public.revoke_support_session(text);
CREATE OR REPLACE FUNCTION public.revoke_support_session(
  p_token text
)
RETURNS boolean
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  v_admin_id uuid;
  v_role     text;
BEGIN
  v_admin_id := auth.uid();
  IF v_admin_id IS NULL THEN
    RAISE EXCEPTION 'Não autenticado.' USING ERRCODE = 'P0001';
  END IF;

  SELECT role INTO v_role FROM public.profiles WHERE id = v_admin_id;
  IF v_role <> 'saas_admin' THEN
    RAISE EXCEPTION 'Acesso negado.' USING ERRCODE = 'P0002';
  END IF;

  DELETE FROM public.support_sessions
   WHERE token = p_token
     AND admin_id = v_admin_id;

  RETURN FOUND;
END;
$$;

-- ── Cleanup automático (opcional) ────────────────────────────────────────────
-- Sessões expiradas podem ser limpas periodicamente via pg_cron ou edge function.
-- DELETE FROM public.support_sessions WHERE expires_at < now();
