-- ==========================================================================
-- Agencia PSI — Migracao: Telefones polimorficos com tipo + principal
-- ==========================================================================
-- Criado por: Leonardo Nohama
-- Data: 2026-04-21 · Sao Carlos/SP — Brasil
--
-- Substitui campos fixos de telefone (patients.telefone, medicos.telefone_*)
-- por estrutura flexivel:
--
--   - contact_types    → tipos configuraveis (Celular, Fixo, WhatsApp, ...)
--                        System (tenant_id NULL) + custom por tenant
--   - contact_phones   → telefones polimorficos (entity_type + entity_id)
--                        Suporta patient, medico, futuramente emergency, etc
--
-- Ate 1 telefone marcado como is_primary por entidade (UNIQUE parcial).
-- Triggers mantem patients.telefone, telefone_alternativo, medicos.telefone_*
-- sincronizados pra nao quebrar codigo legado.
-- ==========================================================================

-- ---------------------------------------------------------------------------
-- Tabela: contact_types
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.contact_types (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID REFERENCES public.tenants(id) ON DELETE CASCADE,  -- NULL = system
    name TEXT NOT NULL CHECK (length(name) > 0 AND length(name) <= 40),
    slug TEXT NOT NULL CHECK (slug ~ '^[a-z0-9_-]{1,40}$'),
    icon TEXT,                          -- classe primeicons (ex: 'pi pi-mobile')
    is_mobile BOOLEAN NOT NULL DEFAULT true,  -- true = mascara celular; false = mascara fixo
    is_system BOOLEAN NOT NULL DEFAULT false,
    position INT NOT NULL DEFAULT 100,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_contact_types_tenant_slug
    ON public.contact_types (tenant_id, slug)
    WHERE tenant_id IS NOT NULL;

CREATE UNIQUE INDEX IF NOT EXISTS uq_contact_types_system_slug
    ON public.contact_types (slug)
    WHERE tenant_id IS NULL;

CREATE INDEX IF NOT EXISTS idx_contact_types_tenant
    ON public.contact_types (tenant_id, position);

DROP TRIGGER IF EXISTS trg_contact_types_updated_at ON public.contact_types;
CREATE TRIGGER trg_contact_types_updated_at
    BEFORE UPDATE ON public.contact_types
    FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();

COMMENT ON TABLE public.contact_types IS
    'Tipos de contato (Celular, Fixo, WhatsApp, ...). System (tenant_id NULL) visiveis a todos; custom por tenant.';

-- Seed: tipos system padrao
INSERT INTO public.contact_types (tenant_id, name, slug, icon, is_mobile, is_system, position) VALUES
    (NULL, 'Celular',      'celular',      'pi pi-mobile',           true,  true, 10),
    (NULL, 'WhatsApp',     'whatsapp',     'pi pi-whatsapp',         true,  true, 20),
    (NULL, 'Fixo',         'fixo',         'pi pi-phone',            false, true, 30),
    (NULL, 'Residencial',  'residencial',  'pi pi-home',             false, true, 40),
    (NULL, 'Comercial',    'comercial',    'pi pi-building',         true,  true, 50),
    (NULL, 'Fax',          'fax',          'pi pi-print',            false, true, 60)
ON CONFLICT DO NOTHING;

-- ---------------------------------------------------------------------------
-- Tabela: contact_phones (polimorfica)
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.contact_phones (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,

    entity_type TEXT NOT NULL CHECK (entity_type IN ('patient', 'medico')),
    entity_id UUID NOT NULL,

    contact_type_id UUID NOT NULL REFERENCES public.contact_types(id) ON DELETE RESTRICT,

    number TEXT NOT NULL CHECK (number ~ '^\d{8,15}$'),  -- digits only, 8-15 (DDI+DDD+num)
    is_primary BOOLEAN NOT NULL DEFAULT false,

    -- Vinculado automaticamente via drawer de conversa (CRM 3.5)
    whatsapp_linked_at TIMESTAMPTZ,

    notes TEXT,
    position INT NOT NULL DEFAULT 100,

    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_contact_phones_entity
    ON public.contact_phones (tenant_id, entity_type, entity_id, position);

CREATE INDEX IF NOT EXISTS idx_contact_phones_number
    ON public.contact_phones (tenant_id, number);

-- Partial unique: apenas 1 primary por entidade
CREATE UNIQUE INDEX IF NOT EXISTS uq_contact_phones_primary
    ON public.contact_phones (entity_type, entity_id)
    WHERE is_primary = true;

DROP TRIGGER IF EXISTS trg_contact_phones_updated_at ON public.contact_phones;
CREATE TRIGGER trg_contact_phones_updated_at
    BEFORE UPDATE ON public.contact_phones
    FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();

COMMENT ON TABLE public.contact_phones IS
    'Telefones polimorficos (patients, medicos, ...). Max 1 primary por entidade. Triggers sincronizam campos legados.';

-- ---------------------------------------------------------------------------
-- Helper: pega o telefone primary (ou primeiro) de uma entidade
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION public.get_entity_primary_phone(
    p_entity_type TEXT,
    p_entity_id UUID
) RETURNS TEXT
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
    SELECT number FROM public.contact_phones
    WHERE entity_type = p_entity_type
      AND entity_id = p_entity_id
    ORDER BY is_primary DESC, position ASC, created_at ASC
    LIMIT 1;
$$;

REVOKE ALL ON FUNCTION public.get_entity_primary_phone(TEXT, UUID) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.get_entity_primary_phone(TEXT, UUID) TO authenticated, service_role;

-- ---------------------------------------------------------------------------
-- Trigger: sincroniza campos legados de patients/medicos apos mudanca
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION public.sync_legacy_phone_fields() RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_entity_type TEXT;
    v_entity_id UUID;
    v_primary TEXT;
    v_secondary TEXT;
    v_whatsapp_slug TEXT;
    v_whatsapp TEXT;
BEGIN
    -- Identifica entidade afetada (pode ser OLD em delete)
    IF TG_OP = 'DELETE' THEN
        v_entity_type := OLD.entity_type;
        v_entity_id := OLD.entity_id;
    ELSE
        v_entity_type := NEW.entity_type;
        v_entity_id := NEW.entity_id;
    END IF;

    -- Pega primary (ou primeiro)
    SELECT number INTO v_primary
        FROM public.contact_phones
        WHERE entity_type = v_entity_type AND entity_id = v_entity_id
        ORDER BY is_primary DESC, position ASC, created_at ASC
        LIMIT 1;

    -- Pega segundo (depois do primary)
    SELECT number INTO v_secondary
        FROM public.contact_phones
        WHERE entity_type = v_entity_type AND entity_id = v_entity_id
          AND is_primary = false
        ORDER BY position ASC, created_at ASC
        OFFSET 0
        LIMIT 1;

    -- Sincroniza campos legados
    IF v_entity_type = 'patient' THEN
        UPDATE public.patients
            SET telefone = v_primary,
                telefone_alternativo = v_secondary
            WHERE id = v_entity_id;
    ELSIF v_entity_type = 'medico' THEN
        -- Medicos: telefone_profissional = primary; telefone_pessoal = secundario
        UPDATE public.medicos
            SET telefone_profissional = v_primary,
                telefone_pessoal = v_secondary
            WHERE id = v_entity_id;
    END IF;

    IF TG_OP = 'DELETE' THEN
        RETURN OLD;
    ELSE
        RETURN NEW;
    END IF;
END;
$$;

DROP TRIGGER IF EXISTS trg_contact_phones_sync_legacy ON public.contact_phones;
CREATE TRIGGER trg_contact_phones_sync_legacy
    AFTER INSERT OR UPDATE OR DELETE ON public.contact_phones
    FOR EACH ROW EXECUTE FUNCTION public.sync_legacy_phone_fields();

-- ---------------------------------------------------------------------------
-- Backfill: migra dados existentes pra contact_phones
-- ---------------------------------------------------------------------------
-- Patients: telefone → Celular primary, telefone_alternativo → Fixo
DO $$
DECLARE
    v_celular_id UUID;
    v_fixo_id UUID;
    v_profissional_id UUID;
BEGIN
    SELECT id INTO v_celular_id FROM public.contact_types WHERE slug = 'celular' AND tenant_id IS NULL LIMIT 1;
    SELECT id INTO v_fixo_id FROM public.contact_types WHERE slug = 'fixo' AND tenant_id IS NULL LIMIT 1;
    SELECT id INTO v_profissional_id FROM public.contact_types WHERE slug = 'comercial' AND tenant_id IS NULL LIMIT 1;

    -- Patients.telefone → Celular primary
    INSERT INTO public.contact_phones (tenant_id, entity_type, entity_id, contact_type_id, number, is_primary, position)
    SELECT
        p.tenant_id,
        'patient',
        p.id,
        v_celular_id,
        regexp_replace(p.telefone, '\D', '', 'g'),
        true,
        10
    FROM public.patients p
    WHERE p.telefone IS NOT NULL
      AND length(regexp_replace(p.telefone, '\D', '', 'g')) BETWEEN 8 AND 15
      AND NOT EXISTS (
        SELECT 1 FROM public.contact_phones cp
        WHERE cp.entity_type = 'patient' AND cp.entity_id = p.id
    )
    ON CONFLICT DO NOTHING;

    -- Patients.telefone_alternativo → Fixo
    INSERT INTO public.contact_phones (tenant_id, entity_type, entity_id, contact_type_id, number, is_primary, position)
    SELECT
        p.tenant_id,
        'patient',
        p.id,
        v_fixo_id,
        regexp_replace(p.telefone_alternativo, '\D', '', 'g'),
        false,
        20
    FROM public.patients p
    WHERE p.telefone_alternativo IS NOT NULL
      AND length(regexp_replace(p.telefone_alternativo, '\D', '', 'g')) BETWEEN 8 AND 15
      AND NOT EXISTS (
        SELECT 1 FROM public.contact_phones cp
        WHERE cp.entity_type = 'patient' AND cp.entity_id = p.id
          AND cp.number = regexp_replace(p.telefone_alternativo, '\D', '', 'g')
    )
    ON CONFLICT DO NOTHING;

    -- Medicos.telefone_profissional → Comercial primary
    INSERT INTO public.contact_phones (tenant_id, entity_type, entity_id, contact_type_id, number, is_primary, position)
    SELECT
        m.tenant_id,
        'medico',
        m.id,
        v_profissional_id,
        regexp_replace(m.telefone_profissional, '\D', '', 'g'),
        true,
        10
    FROM public.medicos m
    WHERE m.telefone_profissional IS NOT NULL
      AND length(regexp_replace(m.telefone_profissional, '\D', '', 'g')) BETWEEN 8 AND 15
      AND NOT EXISTS (
        SELECT 1 FROM public.contact_phones cp
        WHERE cp.entity_type = 'medico' AND cp.entity_id = m.id
    )
    ON CONFLICT DO NOTHING;

    -- Medicos.telefone_pessoal → Celular
    INSERT INTO public.contact_phones (tenant_id, entity_type, entity_id, contact_type_id, number, is_primary, position)
    SELECT
        m.tenant_id,
        'medico',
        m.id,
        v_celular_id,
        regexp_replace(m.telefone_pessoal, '\D', '', 'g'),
        false,
        20
    FROM public.medicos m
    WHERE m.telefone_pessoal IS NOT NULL
      AND length(regexp_replace(m.telefone_pessoal, '\D', '', 'g')) BETWEEN 8 AND 15
      AND NOT EXISTS (
        SELECT 1 FROM public.contact_phones cp
        WHERE cp.entity_type = 'medico' AND cp.entity_id = m.id
          AND cp.number = regexp_replace(m.telefone_pessoal, '\D', '', 'g')
    )
    ON CONFLICT DO NOTHING;
END $$;

-- ---------------------------------------------------------------------------
-- RLS: contact_types
-- ---------------------------------------------------------------------------
ALTER TABLE public.contact_types ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS "contact_types: select" ON public.contact_types;
CREATE POLICY "contact_types: select"
    ON public.contact_types FOR SELECT TO authenticated
    USING (
        tenant_id IS NULL
        OR public.is_saas_admin()
        OR EXISTS (
            SELECT 1 FROM public.tenant_members tm
            WHERE tm.user_id = auth.uid() AND tm.tenant_id = contact_types.tenant_id AND tm.status = 'active'
        )
    );

DROP POLICY IF EXISTS "contact_types: manage custom" ON public.contact_types;
CREATE POLICY "contact_types: manage custom"
    ON public.contact_types FOR ALL TO authenticated
    USING (
        is_system = false AND tenant_id IS NOT NULL AND (
            public.is_saas_admin()
            OR EXISTS (
                SELECT 1 FROM public.tenant_members tm
                WHERE tm.user_id = auth.uid() AND tm.tenant_id = contact_types.tenant_id AND tm.status = 'active'
            )
        )
    )
    WITH CHECK (
        is_system = false AND tenant_id IS NOT NULL AND (
            public.is_saas_admin()
            OR EXISTS (
                SELECT 1 FROM public.tenant_members tm
                WHERE tm.user_id = auth.uid() AND tm.tenant_id = contact_types.tenant_id AND tm.status = 'active'
            )
        )
    );

-- ---------------------------------------------------------------------------
-- RLS: contact_phones
-- ---------------------------------------------------------------------------
ALTER TABLE public.contact_phones ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS "contact_phones: all tenant" ON public.contact_phones;
CREATE POLICY "contact_phones: all tenant"
    ON public.contact_phones FOR ALL TO authenticated
    USING (
        public.is_saas_admin()
        OR EXISTS (
            SELECT 1 FROM public.tenant_members tm
            WHERE tm.user_id = auth.uid() AND tm.tenant_id = contact_phones.tenant_id AND tm.status = 'active'
        )
    )
    WITH CHECK (
        public.is_saas_admin()
        OR EXISTS (
            SELECT 1 FROM public.tenant_members tm
            WHERE tm.user_id = auth.uid() AND tm.tenant_id = contact_phones.tenant_id AND tm.status = 'active'
        )
    );

-- ==========================================================================
-- FIM DA MIGRACAO
-- ==========================================================================
