-- fn_update_market_price_stats, fn_update_tuv_usage_stats, fn_get_predicted_price
-- (rozšířený horizont plánování – predikce cen a TUV)

CREATE OR REPLACE FUNCTION ems.fn_update_market_price_stats(
    p_site_id INT,
    p_lookback_days INT DEFAULT 90
)
RETURNS INT
LANGUAGE plpgsql
AS $$
DECLARE v_count INT;
BEGIN
    INSERT INTO ems.market_price_stats
        (site_id, day_of_week, hour_of_day,
         avg_price, stddev_price, p25_price, p75_price,
         sample_count, last_updated)
    SELECT
        p_site_id,
        EXTRACT(DOW  FROM interval_start AT TIME ZONE 'Europe/Prague')::INT,
        EXTRACT(HOUR FROM interval_start AT TIME ZONE 'Europe/Prague')::INT,
        AVG(buy_raw_price_czk_kwh),
        STDDEV(buy_raw_price_czk_kwh),
        PERCENTILE_CONT(0.25) WITHIN GROUP (
            ORDER BY buy_raw_price_czk_kwh),
        PERCENTILE_CONT(0.75) WITHIN GROUP (
            ORDER BY buy_raw_price_czk_kwh),
        COUNT(*)::INT,
        now()
    FROM ems.market_interval_price
    WHERE market_source IN ('OTE_CZ', 'OTE_CZ_DAM')
      AND interval_start >= now() - make_interval(days => p_lookback_days)
    GROUP BY
        EXTRACT(DOW  FROM interval_start AT TIME ZONE 'Europe/Prague'),
        EXTRACT(HOUR FROM interval_start AT TIME ZONE 'Europe/Prague')
    HAVING COUNT(*) >= 4
    ON CONFLICT (site_id, day_of_week, hour_of_day) DO UPDATE SET
        avg_price    = 0.7 * ems.market_price_stats.avg_price
                     + 0.3 * EXCLUDED.avg_price,
        stddev_price = COALESCE(
            0.7 * ems.market_price_stats.stddev_price
            + 0.3 * EXCLUDED.stddev_price,
            EXCLUDED.stddev_price),
        p25_price    = EXCLUDED.p25_price,
        p75_price    = EXCLUDED.p75_price,
        sample_count = ems.market_price_stats.sample_count
                     + EXCLUDED.sample_count,
        last_updated = now();

    GET DIAGNOSTICS v_count = ROW_COUNT;
    RETURN v_count;
END;
$$;

COMMENT ON FUNCTION ems.fn_update_market_price_stats IS
'Aktualizuje historické průměry spotové ceny per DOW+hodina.
EMA 70/30 pro postupné zpřesňování. Volat denně po importu OTE.
Pro první naplnění: SELECT ems.fn_update_market_price_stats(2, 180);';


CREATE OR REPLACE FUNCTION ems.fn_update_tuv_usage_stats(
    p_site_id INT,
    p_lookback_days INT DEFAULT 30
)
RETURNS INT
LANGUAGE plpgsql
AS $$
DECLARE v_count INT;
BEGIN
    -- Telemetrie TČ je idle-skip (telemetry_collector): rozestup řádků je
    -- 1–14 min. Delta se proto normalizuje na °C/min (gap_min); hustá 1min
    -- data dávají identické hodnoty jako dřív. Mezery > 30 min (výpadek
    -- sběru) se z učení vylučují.
    insert into ems.tuv_usage_stats
        (site_id, day_of_week, hour_of_day,
         avg_temp_delta_c, stddev_temp_delta,
         sample_count, last_updated)
    with deltas as (
        select
            measured_at,
            extract(dow  from measured_at at time zone 'Europe/Prague')::int as dow,
            extract(hour from measured_at at time zone 'Europe/Prague')::int as hour,
            tuv_tank_temp_c - lag(tuv_tank_temp_c) over w as temp_delta_c,
            extract(epoch from measured_at - lag(measured_at) over w) / 60.0 as gap_min
        from ems.telemetry_heat_pump
        where site_id = p_site_id
          and measured_at >= now() - make_interval(days => p_lookback_days)
          and tuv_tank_temp_c is not null
        window w as (partition by site_id order by measured_at)
    )
    select
        p_site_id, dow, hour,
        avg(temp_delta_c / gap_min),
        stddev(temp_delta_c / gap_min),
        count(*)::int,
        now()
    from deltas
    where temp_delta_c is not null
      and gap_min > 0
      and gap_min <= 30
      and abs(temp_delta_c) < 5
    group by dow, hour
    having count(*) >= 4
    ON CONFLICT (site_id, day_of_week, hour_of_day) DO UPDATE SET
        avg_temp_delta_c  = 0.7 * ems.tuv_usage_stats.avg_temp_delta_c
                          + 0.3 * EXCLUDED.avg_temp_delta_c,
        stddev_temp_delta = COALESCE(
            0.7 * ems.tuv_usage_stats.stddev_temp_delta
            + 0.3 * EXCLUDED.stddev_temp_delta,
            EXCLUDED.stddev_temp_delta),
        sample_count      = ems.tuv_usage_stats.sample_count
                          + EXCLUDED.sample_count,
        last_updated      = now();

    GET DIAGNOSTICS v_count = ROW_COUNT;
    RETURN v_count;
END;
$$;

COMMENT ON FUNCTION ems.fn_update_tuv_usage_stats IS
'Aktualizuje statistiku poklesu teploty TUV zásobníku per DOW+hodina.
avg_temp_delta_c je v °C/min (delta normalizovaná délkou mezery mezi řádky —
robustní vůči idle-skip telemetrii; mezery > 30 min vyloučeny).
Záporné avg_temp_delta_c = zásobník se ochlazuje (spotřeba teplé vody).
Potřeba min. 1 měsíc telemetrie TČ. Volat denně.';


CREATE OR REPLACE FUNCTION ems.fn_get_predicted_price(
    p_site_id        INT,
    p_interval_start TIMESTAMPTZ
)
RETURNS NUMERIC
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
    v_dow   INT;
    v_hour  INT;
    v_price NUMERIC;
    v_corr  NUMERIC := 1.0;
BEGIN
    v_dow  := EXTRACT(DOW  FROM p_interval_start AT TIME ZONE 'Europe/Prague')::INT;
    v_hour := EXTRACT(HOUR FROM p_interval_start AT TIME ZONE 'Europe/Prague')::INT;

    SELECT avg_price INTO v_price
    FROM ems.market_price_stats
    WHERE site_id     = p_site_id
      AND day_of_week = v_dow
      AND hour_of_day = v_hour;

    IF v_price IS NULL THEN
        RETURN 2.50;
    END IF;

    RETURN ROUND(v_price * v_corr, 6);
END;
$$;

COMMENT ON FUNCTION ems.fn_get_predicted_price IS
'Vrátí predikovanou cenu pro slot za horizontem OTE.
Zatím používá jen sezónní průměr. Fáze 3d přidá korekci počasím.
Fallback 2.50 Kč/kWh pokud nejsou historická data (min. 3 měsíce).';
