-- =============================================================================
-- Freemium F1 — Enforcement de limite de plano (pacientes), schema-per-tenant
--
-- ⚠️ APLICAR COMO supabase_admin (anexa triggers em tabelas tenant + a função de
--    wiring trg_attach_business_triggers é owned por supabase_admin).
--
-- Trigger genérico BEFORE INSERT em <schema>.patients que:
--   1. resolve o tenant pelo NOME DO SCHEMA (TG_TABLE_SCHEMA → tenant_schemas);
--   2. resolve o plano ATIVO do tenant em runtime (clínica via tenant_id;
--      pessoal/terapeuta via owner user_id — as 6 subs pessoais têm tenant_id NULL);
--   3. lê o limite max_patients de plan_features.limits EM RUNTIME (mudar o número
--      no painel passa a valer sem deploy);
--   4. conta pacientes vivos (status <> 'Arquivado') e dá RAISE parseável
--      'PLAN_LIMIT_REACHED|patients|<limite>' quando já atingiu.
--
-- Sem plano ativo OU sem limite definido (planos PRO) ⇒ não bloqueia.
-- Idempotente (CREATE OR REPLACE + DROP TRIGGER IF EXISTS). Tudo em public
-- (subscriptions/plan_features/tenant_schemas são globais) ⇒ sobrevive ao DROP F6.3.
-- =============================================================================

BEGIN;

-- 1) Resolve o plano ativo de um tenant (clínica OU pessoal) ------------------
CREATE OR REPLACE FUNCTION public.tenant_active_plan_id(p_tenant_id uuid)
RETURNS uuid
LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
    SELECT COALESCE(
        -- clínica: subscription chaveada por tenant_id
        (SELECT vas.plan_id
           FROM public.v_tenant_active_subscription vas
          WHERE vas.tenant_id = p_tenant_id),
        -- pessoal: subscription chaveada pelo owner (user_id), tenant_id NULL
        (SELECT s.plan_id
           FROM public.subscriptions s
           JOIN public.tenant_members tm
             ON tm.user_id = s.user_id
            AND tm.tenant_id = p_tenant_id
            AND tm.status = 'active'
          WHERE s.status = 'active'
            AND s.tenant_id IS NULL
            AND (s.current_period_end IS NULL OR s.current_period_end > now())
          ORDER BY s.created_at DESC
          LIMIT 1)
    );
$$;
ALTER FUNCTION public.tenant_active_plan_id(uuid) OWNER TO supabase_admin;

-- 2) Lê um limite numérico do plano (busca a chave em qualquer feature) -------
--    Ex.: clinic_free guarda max_patients sob clinic_calendar; therapist_free
--    sob patients.manage. Retorna o MIN (mais restritivo) se houver mais de um.
CREATE OR REPLACE FUNCTION public.plan_feature_limit(p_plan_id uuid, p_limit_key text)
RETURNS int
LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
    SELECT min((pf.limits->>p_limit_key)::int)
      FROM public.plan_features pf
     WHERE pf.plan_id = p_plan_id
       AND pf.enabled
       AND pf.limits ? p_limit_key
       AND (pf.limits->>p_limit_key) ~ '^[0-9]+$';
$$;
ALTER FUNCTION public.plan_feature_limit(uuid, text) OWNER TO supabase_admin;

-- 3) Trigger function de enforcement -----------------------------------------
CREATE OR REPLACE FUNCTION public.enforce_patient_plan_limit()
RETURNS trigger
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE
    v_tenant uuid;
    v_plan   uuid;
    v_limit  int;
    v_count  int;
BEGIN
    SELECT tenant_id INTO v_tenant
      FROM public.tenant_schemas
     WHERE schema_name = TG_TABLE_SCHEMA;
    IF v_tenant IS NULL THEN RETURN NEW; END IF;          -- schema não-tenant: ignora

    v_plan := public.tenant_active_plan_id(v_tenant);
    IF v_plan IS NULL THEN RETURN NEW; END IF;            -- sem plano ativo: não bloqueia

    v_limit := public.plan_feature_limit(v_plan, 'max_patients');
    IF v_limit IS NULL THEN RETURN NEW; END IF;           -- plano sem limite (PRO): ilimitado

    EXECUTE format(
        'SELECT count(*) FROM %I.patients WHERE status IS DISTINCT FROM %L',
        TG_TABLE_SCHEMA, 'Arquivado'
    ) INTO v_count;

    IF v_count >= v_limit THEN
        RAISE EXCEPTION 'PLAN_LIMIT_REACHED|patients|%', v_limit USING ERRCODE = 'P0001';
    END IF;

    RETURN NEW;
END $$;
ALTER FUNCTION public.enforce_patient_plan_limit() OWNER TO supabase_admin;

-- 4) Attach helper (pendura o trigger no patients de um schema) ---------------
CREATE OR REPLACE FUNCTION public.attach_plan_limit_triggers(p_schema text)
RETURNS int
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
BEGIN
    IF p_schema NOT LIKE 'tenant\_%' THEN
        RAISE EXCEPTION 'schema inválido %', p_schema;
    END IF;
    IF EXISTS (
        SELECT 1 FROM information_schema.tables
         WHERE table_schema = p_schema AND table_name = 'patients'
    ) THEN
        EXECUTE format('DROP TRIGGER IF EXISTS enforce_patient_plan_limit ON %I.patients', p_schema);
        EXECUTE format(
            'CREATE TRIGGER enforce_patient_plan_limit BEFORE INSERT ON %I.patients '
            'FOR EACH ROW EXECUTE FUNCTION public.enforce_patient_plan_limit()', p_schema);
        RETURN 1;
    END IF;
    RETURN 0;
END $$;
ALTER FUNCTION public.attach_plan_limit_triggers(text) OWNER TO supabase_admin;
GRANT EXECUTE ON FUNCTION public.attach_plan_limit_triggers(text) TO postgres, service_role;

-- 5) Wiring: tenants NOVOS ganham o trigger de limite no clone ----------------
CREATE OR REPLACE FUNCTION public.trg_attach_business_triggers()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
BEGIN
    PERFORM public.attach_agnostic_triggers(NEW.schema_name);
    PERFORM public.attach_schema_aware_triggers(NEW.schema_name);
    PERFORM public.attach_notif_triggers(NEW.schema_name);
    PERFORM public.attach_plan_limit_triggers(NEW.schema_name);
    RETURN NULL;
END $$;
ALTER FUNCTION public.trg_attach_business_triggers() OWNER TO supabase_admin;

-- 6) Backfill: os 9 schemas já existentes ganham o trigger agora -------------
DO $$
DECLARE r record; n int := 0;
BEGIN
    FOR r IN SELECT schema_name FROM public.tenant_schemas LOOP
        n := n + public.attach_plan_limit_triggers(r.schema_name);
    END LOOP;
    RAISE NOTICE 'enforce_patient_plan_limit anexado em % schemas', n;
END $$;

COMMIT;
