-- OTE CZ: signály pro "briefing" dalšího dne (TZ Europe/Prague)
--
-- Cíl: vrátit pár srozumitelných signálů (negativní/okolo nuly/špička),
-- které se dají posílat do Discordu bez logiky v Pythonu.

create or replace function ems.fn_ote_day_signals_prague(
  p_day date,
  p_lookback_days int default 14
)
returns jsonb
language sql
stable
as $fn$
  with params as (
    select
      greatest(1, least(coalesce(p_lookback_days, 14), 60))::int as lookback_days,
      0.25::numeric as zeroish_abs_czk_kwh,
      4::numeric as spike_interesting_czk_kwh,
      6::numeric as spike_extreme_czk_kwh
  ),
  day_slots as (
    select
      mip.interval_start,
      (mip.interval_start at time zone 'Europe/Prague') as local_ts,
      extract(hour from mip.interval_start at time zone 'Europe/Prague')::int as local_hour,
      mip.buy_raw_price_czk_kwh as price
    from ems.market_interval_price mip
    where mip.market_source = 'OTE_CZ'
      and (mip.interval_start at time zone 'Europe/Prague')::date = p_day
  ),
  day_agg as (
    select
      count(*)::int as slot_count,
      min(price) as min_price,
      max(price) as max_price,
      avg(price) as avg_price,
      count(*) filter (where price < 0)::int as neg_slots,
      count(*) filter (where abs(price) <= (select zeroish_abs_czk_kwh from params))::int as zeroish_slots,
      min(price) filter (where local_hour between 10 and 14) as noon_min_price,
      avg(price) filter (where local_hour between 10 and 14) as noon_avg_price,
      max(price) filter (where local_hour between 6 and 9) as morning_max_price,
      max(price) filter (where local_hour between 17 and 21) as evening_max_price,
      max(price) filter (where local_hour between 17 and 21)
        - avg(price) filter (where local_hour between 10 and 14) as evening_minus_noon
    from day_slots
  ),
  hist_hours as (
    select
      (mip.interval_start at time zone 'Europe/Prague')::date as d,
      extract(hour from mip.interval_start at time zone 'Europe/Prague')::int as h,
      avg(mip.buy_raw_price_czk_kwh) as avg_price
    from ems.market_interval_price mip
    cross join params p
    where mip.market_source = 'OTE_CZ'
      and (mip.interval_start at time zone 'Europe/Prague')::date < p_day
      and (mip.interval_start at time zone 'Europe/Prague')::date >= p_day - (p.lookback_days || ' days')::interval
    group by 1, 2
  ),
  hist_windows as (
    select
      avg(avg_price) filter (where h between 17 and 21) as avg_evening,
      avg(avg_price) filter (where h between 6 and 9) as avg_morning,
      avg(avg_price) filter (where h between 10 and 14) as avg_noon
    from hist_hours
  ),
  signals as (
    select jsonb_agg(s order by (s ->> 'severity') desc, (s ->> 'code')) as arr
    from (
      -- negativní
      select jsonb_build_object(
        'code', 'NEG_EXTREME',
        'severity', 3,
        'title', 'extrémně záporné ceny',
        'detail', format('min %s Kč/kWh, záporné sloty %s', round((select min_price from day_agg), 3), (select neg_slots from day_agg))
      ) s
      where (select min_price from day_agg) <= -0.50
         or (select neg_slots from day_agg) >= 8

      union all
      select jsonb_build_object(
        'code', 'NEG_PRESENT',
        'severity', 2,
        'title', 'záporné ceny',
        'detail', format('min %s Kč/kWh, záporné sloty %s', round((select min_price from day_agg), 3), (select neg_slots from day_agg))
      ) s
      where (select min_price from day_agg) < 0
        and not (
          (select min_price from day_agg) <= -0.50
          or (select neg_slots from day_agg) >= 8
        )

      -- okolo nuly přes den
      union all
      select jsonb_build_object(
        'code', 'NOON_ZEROISH',
        'severity', 2,
        'title', 'poledne okolo nuly',
        'detail', format('polední průměr %s Kč/kWh (10–14)', round((select noon_avg_price from day_agg), 3))
      ) s
      where coalesce((select noon_avg_price from day_agg), 999) <= (select zeroish_abs_czk_kwh from params)

      union all
      select jsonb_build_object(
        'code', 'MANY_ZEROISH',
        'severity', 1,
        'title', 'hodně slotů okolo nuly',
        'detail', format('okolo nuly slotů %s (|p| ≤ %s Kč/kWh)', (select zeroish_slots from day_agg), round((select zeroish_abs_czk_kwh from params), 2))
      ) s
      where (select zeroish_slots from day_agg) >= 16

      -- špička večer (hard)
      union all
      select jsonb_build_object(
        'code', 'EVENING_SPIKE_EXTREME',
        'severity', 3,
        'title', 'večer extrémně drahý',
        'detail', format('max večer %s Kč/kWh (17–21)', round((select evening_max_price from day_agg), 3))
      ) s
      where coalesce((select evening_max_price from day_agg), 0) >= (select spike_extreme_czk_kwh from params)

      union all
      select jsonb_build_object(
        'code', 'EVENING_SPIKE_INTERESTING',
        'severity', 2,
        'title', 'večer drahý',
        'detail', format('max večer %s Kč/kWh (17–21)', round((select evening_max_price from day_agg), 3))
      ) s
      where coalesce((select evening_max_price from day_agg), 0) >= (select spike_interesting_czk_kwh from params)
        and coalesce((select evening_max_price from day_agg), 0) < (select spike_extreme_czk_kwh from params)

      -- špička večer (relativní vůči posledním N dnům)
      union all
      select jsonb_build_object(
        'code', 'EVENING_SPIKE_REL',
        'severity', 2,
        'title', 'večer nadprůměrná špička',
        'detail', format(
          'max večer %s vs. průměr %s (lookback %s dní)',
          round((select evening_max_price from day_agg), 3),
          round((select avg_evening from hist_windows), 3),
          (select lookback_days from params)
        )
      ) s
      where (select avg_evening from hist_windows) is not null
        and coalesce((select evening_max_price from day_agg), 0) >= (select avg_evening from hist_windows) + 1.5
    ) t
  )
  select jsonb_build_object(
    'day', p_day,
    'lookback_days', (select lookback_days from params),
    'metrics', jsonb_build_object(
      'slot_count', (select slot_count from day_agg),
      'min_price', round(coalesce((select min_price from day_agg), 0)::numeric, 6),
      'max_price', round(coalesce((select max_price from day_agg), 0)::numeric, 6),
      'avg_price', round(coalesce((select avg_price from day_agg), 0)::numeric, 6),
      'neg_slots', (select neg_slots from day_agg),
      'zeroish_slots', (select zeroish_slots from day_agg),
      'noon_avg_price', round(coalesce((select noon_avg_price from day_agg), 0)::numeric, 6),
      'evening_max_price', round(coalesce((select evening_max_price from day_agg), 0)::numeric, 6),
      'avg_evening_last_n', round(coalesce((select avg_evening from hist_windows), 0)::numeric, 6),
      'avg_noon_last_n', round(coalesce((select avg_noon from hist_windows), 0)::numeric, 6)
    ),
    'signals', coalesce((select arr from signals), '[]'::jsonb)
  );
$fn$;

comment on function ems.fn_ote_day_signals_prague(date, int) is
  'Signály pro briefing OTE_CZ cen pro p_day (negativní/okolo nuly/špička), včetně relativní špičky proti posledním N dnům.';

