-- =============================================================================
-- Freemium F2 — RPCs idempotentes do self-service (schema-per-tenant)
--
-- ⚠️ APLICAR COMO supabase_admin (auto_provision insere em tenants/members/
--    subscriptions/profiles + roda clone_tenant_template).
--
-- Com confirmação de e-mail LIGADA, o signup NÃO tem sessão — então nada que
-- dependa de auth.uid() roda no signup. A escolha do usuário (nome, slug, plano,
-- intervalo, kind) é gravada no raw_user_meta_data do signUp e PROCESSADA aqui,
-- no 1º login pós-confirmação:
--   • slug_disponivel(p_slug)          → {ok, motivo}      (chamável por anon no signup)
--   • auto_provision_free_tenant(...)  → cria tenant + clone + master + sub free
--   • processar_pos_signup()           → cria a intenção SÓ pro caminho pago
--
-- Todas idempotentes. Não há tabela de aceite legal neste sistema (pulado).
-- =============================================================================

BEGIN;

-- 1) slug_disponivel ---------------------------------------------------------
--    Valida formato (mesma regra do generate_tenant_slug), reservados e uso.
--    Chamável por ANON (signup acontece antes do login). Não vaza dados de
--    tenant além do fato "slug em uso".
CREATE OR REPLACE FUNCTION public.slug_disponivel(p_slug text)
RETURNS jsonb
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE
    v text := lower(trim(coalesce(p_slug, '')));
    v_reservados text[] := ARRAY['public','tenant','admin','www','api','app','auth','supabase','postgres','saas','suporte','support'];
BEGIN
    IF length(v) < 3 THEN
        RETURN jsonb_build_object('ok', false, 'motivo', 'curto');
    END IF;
    IF length(v) > 48 THEN
        RETURN jsonb_build_object('ok', false, 'motivo', 'longo');
    END IF;
    -- começa com letra, só [a-z0-9_]
    IF v !~ '^[a-z][a-z0-9_]*$' THEN
        RETURN jsonb_build_object('ok', false, 'motivo', 'invalido');
    END IF;
    IF v = ANY(v_reservados) THEN
        RETURN jsonb_build_object('ok', false, 'motivo', 'reservado');
    END IF;
    IF EXISTS (SELECT 1 FROM public.tenants WHERE slug = v) THEN
        RETURN jsonb_build_object('ok', false, 'motivo', 'em_uso');
    END IF;
    -- (F3) blacklist de slug integra aqui via motivo 'bloqueado'
    RETURN jsonb_build_object('ok', true, 'motivo', 'disponivel');
END $$;
ALTER FUNCTION public.slug_disponivel(text) OWNER TO supabase_admin;
REVOKE ALL ON FUNCTION public.slug_disponivel(text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.slug_disponivel(text) TO anon, authenticated, service_role;

-- 2) auto_provision_free_tenant ----------------------------------------------
--    Idempotente: se o usuário já tem tenant ativo, retorna esse. Senão lê o
--    raw_user_meta_data, cria o tenant (slug escolhido OU auto), vira master,
--    clona o schema e cria a subscription gratuita ativa (XOR conforme target).
--    p_slug_override permite a tela /onboarding reescolher o slug se colidiu.
CREATE OR REPLACE FUNCTION public.auto_provision_free_tenant(p_slug_override text DEFAULT NULL)
RETURNS jsonb
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE
    v_uid       uuid := auth.uid();
    v_meta      jsonb;
    v_email     text;
    v_kind      text;
    v_acct      text;
    v_name      text;
    v_slug      text;
    v_display   text;
    v_tenant_id uuid;
    v_plan_key  text;
    v_plan_id   uuid;
    v_target    text;
    v_existing  uuid;
BEGIN
    IF v_uid IS NULL THEN
        RAISE EXCEPTION 'sem sessão' USING ERRCODE = '28000';
    END IF;

    -- idempotência: já tem tenant ativo?
    SELECT tm.tenant_id INTO v_existing
    FROM public.tenant_members tm
    WHERE tm.user_id = v_uid AND tm.status = 'active'
    ORDER BY tm.created_at ASC
    LIMIT 1;
    IF v_existing IS NOT NULL THEN
        RETURN jsonb_build_object('status', 'exists', 'tenant_id', v_existing,
                                  'slug', (SELECT slug FROM public.tenants WHERE id = v_existing));
    END IF;

    SELECT au.raw_user_meta_data, au.email INTO v_meta, v_email
    FROM auth.users au WHERE au.id = v_uid;
    v_meta := COALESCE(v_meta, '{}'::jsonb);

    -- kind: do metadata, default therapist (maioria). Valida contra os aceitos.
    v_kind := lower(coalesce(nullif(trim(v_meta->>'account_kind'), ''), 'therapist'));
    IF v_kind NOT IN ('therapist','clinic_coworking','clinic_reception','clinic_full') THEN
        v_kind := 'therapist';
    END IF;
    v_acct := CASE WHEN v_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END;

    v_display := nullif(trim(v_meta->>'display_name'), '');
    v_name := coalesce(
        nullif(trim(v_meta->>'tenant_name'), ''),
        v_display,
        split_part(coalesce(v_email, 'conta'), '@', 1),
        'Conta');

    -- slug: override > metadata > NULL (trigger auto-gera). Valida disponibilidade.
    v_slug := lower(trim(coalesce(p_slug_override, v_meta->>'tenant_slug', '')));
    IF v_slug = '' THEN
        v_slug := NULL;
    ELSE
        IF NOT (public.slug_disponivel(v_slug)->>'ok')::boolean THEN
            RAISE EXCEPTION 'SLUG_TAKEN|%', v_slug USING ERRCODE = 'P0001';
        END IF;
    END IF;

    -- cria tenant (trg_tenants_slug respeita slug fornecido; gera se NULL)
    INSERT INTO public.tenants (name, kind, slug, created_at)
    VALUES (v_name, v_kind, v_slug, now())
    RETURNING id, slug INTO v_tenant_id, v_slug;

    INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at)
    VALUES (v_tenant_id, v_uid, 'tenant_admin', 'active', now());

    UPDATE public.profiles
       SET account_type = v_acct,
           full_name = COALESCE(full_name, v_display)
     WHERE id = v_uid;

    -- provisiona o schema físico + seed
    PERFORM public.clone_tenant_template(v_tenant_id);
    PERFORM public.seed_determined_commitments(v_tenant_id);

    -- subscription gratuita ativa (XOR: clinic→tenant_id; therapist→user_id)
    v_plan_key := CASE WHEN v_acct = 'therapist' THEN 'therapist_free' ELSE 'clinic_free' END;
    SELECT id, lower(target) INTO v_plan_id, v_target FROM public.plans WHERE key = v_plan_key;

    INSERT INTO public.subscriptions (plan_id, plan_key, status, interval, source,
                                      tenant_id, user_id, started_at, activated_at, current_period_start)
    VALUES (v_plan_id, v_plan_key, 'active', 'month', 'auto_free',
            CASE WHEN v_target = 'clinic' THEN v_tenant_id ELSE NULL END,
            CASE WHEN v_target = 'clinic' THEN NULL ELSE v_uid END,
            now(), now(), now());

    RETURN jsonb_build_object('status', 'provisioned', 'tenant_id', v_tenant_id,
                              'slug', v_slug, 'kind', v_kind, 'plan_key', v_plan_key);
END $$;
ALTER FUNCTION public.auto_provision_free_tenant(text) OWNER TO supabase_admin;
REVOKE ALL ON FUNCTION public.auto_provision_free_tenant(text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.auto_provision_free_tenant(text) TO authenticated, service_role;

-- 3) processar_pos_signup ----------------------------------------------------
--    Caminho PAGO: se o usuário escolheu um plano PRO no signup (metadata),
--    registra a intenção (idempotente — uma por usuário+plano 'new'). O caminho
--    gratuito não gera intenção. Sem tabela de aceite legal (pulado).
CREATE OR REPLACE FUNCTION public.processar_pos_signup()
RETURNS jsonb
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE
    v_uid      uuid := auth.uid();
    v_meta     jsonb;
    v_email    text;
    v_plan_key text;
    v_interval text;
    v_plan     record;
    v_tenant   uuid;
    v_amount   int;
BEGIN
    IF v_uid IS NULL THEN
        RAISE EXCEPTION 'sem sessão' USING ERRCODE = '28000';
    END IF;

    SELECT au.raw_user_meta_data, au.email INTO v_meta, v_email
    FROM auth.users au WHERE au.id = v_uid;
    v_meta := COALESCE(v_meta, '{}'::jsonb);

    v_plan_key := nullif(trim(v_meta->>'plan_key'), '');
    v_interval := lower(coalesce(nullif(trim(v_meta->>'billing_interval'), ''), 'month'));
    IF v_interval NOT IN ('month','year') THEN v_interval := 'month'; END IF;

    -- sem plano escolhido OU plano gratuito → nada a fazer
    IF v_plan_key IS NULL OR v_plan_key LIKE '%\_free' THEN
        RETURN jsonb_build_object('status', 'no_intent');
    END IF;

    SELECT * INTO v_plan FROM public.plans WHERE key = v_plan_key AND is_active;
    IF NOT FOUND THEN
        RETURN jsonb_build_object('status', 'plan_not_found', 'plan_key', v_plan_key);
    END IF;

    -- idempotência: já existe intent 'new' desse usuário+plano?
    IF EXISTS (
        SELECT 1 FROM public.subscription_intents
        WHERE created_by_user_id = v_uid AND plan_key = v_plan_key AND status = 'new'
    ) THEN
        RETURN jsonb_build_object('status', 'intent_exists', 'plan_key', v_plan_key);
    END IF;

    SELECT tm.tenant_id INTO v_tenant
    FROM public.tenant_members tm WHERE tm.user_id = v_uid AND tm.status = 'active'
    ORDER BY tm.created_at ASC LIMIT 1;

    v_amount := CASE WHEN v_interval = 'year'
                     THEN COALESCE(v_plan.price_cents, 0) * 12
                     ELSE COALESCE(v_plan.price_cents, 0) END;

    -- escreve direto na tabela real (a view subscription_intents tem INSTEAD OF
    -- trigger que não propaga user_id pra _tenant; o serviço do front também
    -- escreve nas tabelas reais por target).
    IF lower(v_plan.target) = 'clinic' THEN
        INSERT INTO public.subscription_intents_tenant
            (tenant_id, user_id, created_by_user_id, email, plan_id, plan_key,
             interval, amount_cents, currency, status, source)
        VALUES
            (v_tenant, v_uid, v_uid, v_email, v_plan.id, v_plan_key,
             v_interval, v_amount, 'BRL', 'new', 'signup');
    ELSE
        INSERT INTO public.subscription_intents_personal
            (user_id, created_by_user_id, email, plan_id, plan_key,
             interval, amount_cents, currency, status, source)
        VALUES
            (v_uid, v_uid, v_email, v_plan.id, v_plan_key,
             v_interval, v_amount, 'BRL', 'new', 'signup');
    END IF;

    RETURN jsonb_build_object('status', 'intent_created', 'plan_key', v_plan_key, 'interval', v_interval);
END $$;
ALTER FUNCTION public.processar_pos_signup() OWNER TO supabase_admin;
REVOKE ALL ON FUNCTION public.processar_pos_signup() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.processar_pos_signup() TO authenticated, service_role;

COMMIT;
