-- ═══════════════════════════════════════════════════════════════════════════
-- FIX: agendador_slots_disponiveis + agendador_dias_disponiveis
-- Usa agenda_online_slots como fonte de slots
-- Cruzamento com: agenda_eventos, recurrence_rules/exceptions,
--                 agendador_solicitacoes, agenda_bloqueios (feriados/bloqueios)
-- Execute no Supabase SQL Editor
-- ═══════════════════════════════════════════════════════════════════════════

CREATE OR REPLACE FUNCTION public.agendador_slots_disponiveis(
  p_slug text,
  p_data date
)
RETURNS TABLE (hora time, disponivel boolean)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  v_owner_id       uuid;
  v_duracao        int;
  v_antecedencia   int;
  v_agora          timestamptz;
  v_db_dow         int;
  v_slot           time;
  v_slot_fim       time;
  v_slot_ts        timestamptz;
  v_ocupado        boolean;
  -- loop de recorrências
  v_rule           RECORD;
  v_rule_start_dow int;
  v_first_occ      date;
  v_day_diff       int;
  v_ex_type        text;
BEGIN
  SELECT c.owner_id, c.duracao_sessao_min, c.antecedencia_minima_horas
  INTO v_owner_id, v_duracao, v_antecedencia
  FROM public.agendador_configuracoes c
  WHERE c.link_slug = p_slug AND c.ativo = true
  LIMIT 1;

  IF v_owner_id IS NULL THEN RETURN; END IF;

  v_agora  := now();
  v_db_dow := extract(dow from p_data::timestamp)::int;

  -- ── Dia inteiro bloqueado? (agenda_bloqueios sem hora) ───────────────────
  -- Se sim, não há nenhum slot disponível — retorna vazio.
  IF EXISTS (
    SELECT 1 FROM public.agenda_bloqueios b
    WHERE b.owner_id     = v_owner_id
      AND b.data_inicio <= p_data
      AND COALESCE(b.data_fim, p_data) >= p_data
      AND b.hora_inicio IS NULL   -- bloqueio de dia inteiro
      AND (
        (NOT b.recorrente)
        OR (b.recorrente AND b.dia_semana = v_db_dow)
      )
  ) THEN
    RETURN;
  END IF;

  FOR v_slot IN
    SELECT s.time
    FROM public.agenda_online_slots s
    WHERE s.owner_id = v_owner_id
      AND s.weekday  = v_db_dow
      AND s.enabled  = true
    ORDER BY s.time
  LOOP
    v_slot_fim := v_slot + (v_duracao || ' minutes')::interval;
    v_ocupado  := false;

    -- ── Antecedência mínima ──────────────────────────────────────────────────
    v_slot_ts := (p_data::text || ' ' || v_slot::text)::timestamp
                  AT TIME ZONE 'America/Sao_Paulo';
    IF v_slot_ts < v_agora + (v_antecedencia || ' hours')::interval THEN
      v_ocupado := true;
    END IF;

    -- ── Bloqueio de horário específico (agenda_bloqueios com hora) ───────────
    IF NOT v_ocupado THEN
      SELECT EXISTS (
        SELECT 1 FROM public.agenda_bloqueios b
        WHERE b.owner_id     = v_owner_id
          AND b.data_inicio <= p_data
          AND COALESCE(b.data_fim, p_data) >= p_data
          AND b.hora_inicio IS NOT NULL
          AND b.hora_inicio  < v_slot_fim
          AND b.hora_fim     > v_slot
          AND (
            (NOT b.recorrente)
            OR (b.recorrente AND b.dia_semana = v_db_dow)
          )
      ) INTO v_ocupado;
    END IF;

    -- ── Eventos avulsos internos (agenda_eventos) ────────────────────────────
    IF NOT v_ocupado THEN
      SELECT EXISTS (
        SELECT 1 FROM public.agenda_eventos e
        WHERE e.owner_id = v_owner_id
          AND e.status::text NOT IN ('cancelado', 'faltou')
          AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::date = p_data
          AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::time < v_slot_fim
          AND (e.fim_em    AT TIME ZONE 'America/Sao_Paulo')::time > v_slot
      ) INTO v_ocupado;
    END IF;

    -- ── Recorrências ativas (recurrence_rules) ───────────────────────────────
    IF NOT v_ocupado THEN
      FOR v_rule IN
        SELECT
          r.id,
          r.start_date::date                AS start_date,
          r.end_date::date                  AS end_date,
          r.start_time::time                AS start_time,
          r.end_time::time                  AS end_time,
          COALESCE(r.interval, 1)::int      AS interval
        FROM public.recurrence_rules r
        WHERE r.owner_id = v_owner_id
          AND r.status   = 'ativo'
          AND p_data    >= r.start_date::date
          AND (r.end_date IS NULL OR p_data <= r.end_date::date)
          AND v_db_dow = ANY(r.weekdays)
          AND r.start_time::time < v_slot_fim
          AND r.end_time::time   > v_slot
      LOOP
        v_rule_start_dow := extract(dow from v_rule.start_date)::int;
        v_first_occ      := v_rule.start_date
                              + (((v_db_dow - v_rule_start_dow + 7) % 7))::int;
        v_day_diff       := (p_data - v_first_occ)::int;

        IF v_day_diff >= 0 AND v_day_diff % (7 * v_rule.interval) = 0 THEN
          v_ex_type := NULL;
          SELECT ex.type INTO v_ex_type
          FROM public.recurrence_exceptions ex
          WHERE ex.recurrence_id = v_rule.id
            AND ex.original_date = p_data
          LIMIT 1;

          IF v_ex_type IS NULL OR v_ex_type NOT IN (
            'cancel_session', 'patient_missed',
            'therapist_canceled', 'holiday_block',
            'reschedule_session'
          ) THEN
            v_ocupado := true;
            EXIT;
          END IF;
        END IF;
      END LOOP;
    END IF;

    -- ── Recorrências remarcadas para este dia ────────────────────────────────
    IF NOT v_ocupado THEN
      SELECT EXISTS (
        SELECT 1
        FROM public.recurrence_exceptions ex
        JOIN public.recurrence_rules r ON r.id = ex.recurrence_id
        WHERE r.owner_id  = v_owner_id
          AND r.status    = 'ativo'
          AND ex.type     = 'reschedule_session'
          AND ex.new_date = p_data
          AND COALESCE(ex.new_start_time, r.start_time)::time < v_slot_fim
          AND COALESCE(ex.new_end_time,   r.end_time)::time   > v_slot
      ) INTO v_ocupado;
    END IF;

    -- ── Solicitações públicas pendentes ──────────────────────────────────────
    IF NOT v_ocupado THEN
      SELECT EXISTS (
        SELECT 1 FROM public.agendador_solicitacoes sol
        WHERE sol.owner_id        = v_owner_id
          AND sol.status          = 'pendente'
          AND sol.data_solicitada = p_data
          AND sol.hora_solicitada = v_slot
          AND (sol.reservado_ate IS NULL OR sol.reservado_ate > v_agora)
      ) INTO v_ocupado;
    END IF;

    hora       := v_slot;
    disponivel := NOT v_ocupado;
    RETURN NEXT;
  END LOOP;
END;
$$;

GRANT EXECUTE ON FUNCTION public.agendador_slots_disponiveis(text, date) TO anon, authenticated;


-- ── agendador_dias_disponiveis ───────────────────────────────────────────────
CREATE OR REPLACE FUNCTION public.agendador_dias_disponiveis(
  p_slug text,
  p_ano  int,
  p_mes  int
)
RETURNS TABLE (data date, tem_slots boolean)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  v_owner_id     uuid;
  v_antecedencia int;
  v_agora        timestamptz;
  v_data         date;
  v_data_inicio  date;
  v_data_fim     date;
  v_db_dow       int;
  v_tem_slot     boolean;
  v_bloqueado    boolean;
BEGIN
  SELECT c.owner_id, c.antecedencia_minima_horas
  INTO v_owner_id, v_antecedencia
  FROM public.agendador_configuracoes c
  WHERE c.link_slug = p_slug AND c.ativo = true
  LIMIT 1;

  IF v_owner_id IS NULL THEN RETURN; END IF;

  v_agora       := now();
  v_data_inicio := make_date(p_ano, p_mes, 1);
  v_data_fim    := (v_data_inicio + interval '1 month' - interval '1 day')::date;

  v_data := v_data_inicio;
  WHILE v_data <= v_data_fim LOOP
    v_db_dow := extract(dow from v_data::timestamp)::int;

    -- ── Dia inteiro bloqueado? (agenda_bloqueios) ─────────────────────────
    SELECT EXISTS (
      SELECT 1 FROM public.agenda_bloqueios b
      WHERE b.owner_id     = v_owner_id
        AND b.data_inicio <= v_data
        AND COALESCE(b.data_fim, v_data) >= v_data
        AND b.hora_inicio IS NULL   -- bloqueio de dia inteiro
        AND (
          (NOT b.recorrente)
          OR (b.recorrente AND b.dia_semana = v_db_dow)
        )
    ) INTO v_bloqueado;

    IF v_bloqueado THEN
      v_data := v_data + 1;
      CONTINUE;
    END IF;

    -- ── Tem slots disponíveis no dia? ─────────────────────────────────────
    SELECT EXISTS (
      SELECT 1 FROM public.agenda_online_slots s
      WHERE s.owner_id = v_owner_id
        AND s.weekday  = v_db_dow
        AND s.enabled  = true
        AND (v_data::text || ' ' || s.time::text)::timestamp
              AT TIME ZONE 'America/Sao_Paulo'
            >= v_agora + (v_antecedencia || ' hours')::interval
    ) INTO v_tem_slot;

    IF v_tem_slot THEN
      data      := v_data;
      tem_slots := true;
      RETURN NEXT;
    END IF;

    v_data := v_data + 1;
  END LOOP;
END;
$$;

GRANT EXECUTE ON FUNCTION public.agendador_dias_disponiveis(text, int, int) TO anon, authenticated;
