-- Řízení bazénového čerpadla (Phase 1, bez solveru): denní runtime budget z
-- fn_pool_daily_runtime_min (teplotní nebo statický fallback) rozvržený do
-- NEJLEVNĚJŠÍHO souvislého okna dne (efektivní nákupní cena), + dump-load overlay
-- (záporná/nulová výkupní cena → absorbuj přebytek místo exportu se ztrátou).
-- Výstup řídí Shelly relé přes signál POOL_PUMP_ON (fn_signal_enqueue_bool →
-- signal_service). Žádné Modbus. Bazál (R__003) bazén odečítá → s tímto řízením
-- se odečet stává správným (řízená + plánovaná zátěž).

-- Rozhodnutí ON/OFF pro daný 15min slot.
create or replace function ems.fn_pool_schedule_slot(
  p_pump_id int,
  p_slot_start timestamptz
)
returns boolean
language sql
stable
as $fn$
  with cfg as (
    select pp.id, pp.site_id, pp.schedulable,
      greatest(0, coalesce(
        (ems.fn_pool_daily_runtime_min(pp.id) ->> 'runtime_min')::int, 0
      )) as runtime_min
    from ems.asset_pool_pump pp
    where pp.id = p_pump_id
  ),
  win as (
    select c.site_id, ceil(c.runtime_min::numeric / 15.0)::int as w
    from cfg c
  ),
  -- sloty kalendářního dne slotu (Europe/Prague) s efektivní cenou
  day_slots as (
    select ep.interval_start,
      ep.effective_buy_price_czk_kwh as buy,
      ep.effective_sell_price_czk_kwh as sell,
      row_number() over (order by ep.interval_start) as rn
    from ems.vw_site_effective_price ep
    join cfg c on c.site_id = ep.site_id
    where (ep.interval_start at time zone 'Europe/Prague')::date
        = (p_slot_start at time zone 'Europe/Prague')::date
  ),
  -- nejlevnější souvislé okno délky w slotů (self-join, ~96×w řádků = triviální)
  best as (
    select s1.rn as start_rn
    from day_slots s1
    join day_slots s2
      on s2.rn >= s1.rn and s2.rn < s1.rn + (select w from win)
    where (select w from win) > 0
    group by s1.rn
    having count(*) = (select w from win)
    order by sum(s2.buy) asc, s1.rn asc
    limit 1
  )
  select coalesce((select schedulable from cfg), false)
    and coalesce(
      -- v nejlevnějším souvislém okně budgetu
      exists (
        select 1 from day_slots ds
        cross join best b
        where ds.interval_start = p_slot_start
          and ds.rn >= b.start_rn
          and ds.rn < b.start_rn + (select w from win)
      )
      -- NEBO dump-load: záporná/nulová výkupní cena ⇒ raději zkonzumuj než exportuj se ztrátou
      or exists (
        select 1 from day_slots ds
        where ds.interval_start = p_slot_start and ds.sell <= 0
      ),
      false
    );
$fn$;

comment on function ems.fn_pool_schedule_slot is
  'Pool ON/OFF pro 15min slot (Phase 1, bez solveru): nejlevnější souvislé okno délky = daily runtime budget (fn_pool_daily_runtime_min) z vw_site_effective_price, NEBO dump-load při sell<=0. false když pump není schedulable / není cena pro den.';

-- Control tick: pro každý aktivní řiditelný bazén spočti stav slotu a zařaď signál
-- POOL_PUMP_ON (idempotentně). Volá control smyčka každých 15 min (hranice slotu).
-- Enqueue jen když existuje signal_route (jinak bezpečně nic — route se seeduje provozně).
create or replace function ems.fn_pool_control_tick(
  p_now timestamptz default now()
)
returns table(
  pump_id int,
  site_id int,
  desired_on boolean,
  runtime_min int,
  has_route boolean,
  enqueued int
)
language plpgsql
as $fn$
declare
  v_slot timestamptz;
  r record;
  v_on boolean;
  v_route boolean;
begin
  v_slot := date_bin(interval '15 minutes', p_now, timestamptz '1970-01-01T00:00:00Z');
  for r in
    select pp.id as pid, pp.site_id as sid,
      greatest(0, coalesce(
        (ems.fn_pool_daily_runtime_min(pp.id) ->> 'runtime_min')::int, 0
      )) as rt
    from ems.asset_pool_pump pp
    join ems.site s on s.id = pp.site_id
    where s.active = true and pp.schedulable = true
  loop
    v_on := coalesce(ems.fn_pool_schedule_slot(r.pid, v_slot), false);
    v_route := exists (
      select 1 from ems.signal_route sr
      where sr.site_id = r.sid and sr.signal_code = 'POOL_PUMP_ON'
    );
    pump_id := r.pid;
    site_id := r.sid;
    desired_on := v_on;
    runtime_min := r.rt;
    has_route := v_route;
    enqueued := case when v_route
      then ems.fn_signal_enqueue_bool(r.sid, 'POOL_PUMP_ON', v_on)
      else 0
    end;
    return next;
  end loop;
end;
$fn$;

comment on function ems.fn_pool_control_tick is
  'Control tick bazénu (každých 15 min): pro aktivní řiditelné pumpy spočte fn_pool_schedule_slot a zařadí POOL_PUMP_ON (jen když existuje signal_route). Shelly relé pak řídí signal_service. Bez Modbus.';
