-- =============================================================
-- R__fn_fill_audit_interval.sql
-- EMS Platform – plnění audit_interval ze skutečné telemetrie
-- Repeatable migration
-- =============================================================

CREATE OR REPLACE FUNCTION ems.fn_fill_audit_interval(
    p_site_id      INT,
    p_interval_start TIMESTAMPTZ
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
    v_interval_end         TIMESTAMPTZ := p_interval_start + INTERVAL '15 minutes';
    v_run_id               INT;
    v_avg_pv_power_w       INT;
    v_avg_battery_power_w  INT;
    v_avg_grid_power_w     INT;
    v_avg_load_power_w     INT;
    v_last_soc             NUMERIC(5,2);
    v_sum_ev_power_w       INT;
    v_avg_hp_power_w       INT;
    v_plan                 ems.planning_interval%ROWTYPE;
    v_buy_price            NUMERIC;
    v_sell_price           NUMERIC;
    v_actual_cost          NUMERIC := NULL;
    v_green_bonus_czk      NUMERIC := 0;
    v_pv_b_production_wh   NUMERIC;
    v_array_prod_wh        NUMERIC;
    r_bonus                RECORD;
BEGIN
    -- Najít aktivní plán pro tento interval
    SELECT pi.* INTO v_plan
    FROM ems.planning_interval pi
    JOIN ems.planning_run pr ON pr.id = pi.run_id
    WHERE pr.site_id = p_site_id
      AND pi.interval_start = p_interval_start
      AND pr.status IN ('active', 'superseded')
    ORDER BY pr.created_at DESC
    LIMIT 1;

    v_run_id := v_plan.run_id;

    -- Agregovat telemetrii střídače (průměr za 15min; agregace bez GROUP BY vrací vždy 1 řádek)
    SELECT
        AVG(pv_power_w)::INT,
        AVG(battery_power_w)::INT,
        AVG(grid_power_w)::INT,
        AVG(load_power_w)::INT,
        LAST(battery_soc_percent, measured_at)
    INTO
        v_avg_pv_power_w,
        v_avg_battery_power_w,
        v_avg_grid_power_w,
        v_avg_load_power_w,
        v_last_soc
    FROM ems.telemetry_inverter
    WHERE site_id = p_site_id
      AND measured_at >= p_interval_start
      AND measured_at < v_interval_end;

    -- Agregovat EV nabíječky (součet průměrů po charger_id)
    SELECT COALESCE(SUM(avg_power), 0)::INT
    INTO v_sum_ev_power_w
    FROM (
        SELECT AVG(power_w) AS avg_power
        FROM ems.telemetry_ev_charger
        WHERE site_id = p_site_id
          AND measured_at >= p_interval_start
          AND measured_at < v_interval_end
        GROUP BY charger_id
    ) sub;

    -- Agregovat tepelné čerpadlo
    SELECT AVG(power_w)::INT
    INTO v_avg_hp_power_w
    FROM ems.telemetry_heat_pump
    WHERE site_id = p_site_id
      AND measured_at >= p_interval_start
      AND measured_at < v_interval_end;

    -- Efektivní cena pro výpočet skutečných nákladů
    v_buy_price  := ems.fn_effective_buy_price(p_site_id, p_interval_start);
    v_sell_price := ems.fn_effective_sell_price(p_site_id, p_interval_start);

    -- Skutečné náklady (kladný grid = nákup, záporný = prodej)
    IF v_avg_grid_power_w IS NOT NULL THEN
        v_actual_cost := (v_avg_grid_power_w::NUMERIC / 1000.0 / 4.0)
                         * CASE WHEN v_avg_grid_power_w >= 0
                                THEN COALESCE(v_buy_price, 0)
                                ELSE COALESCE(v_sell_price, 0) END;
    END IF;

    -- Zelený bonus: výroba bonusových polí z poslední ok predikce pro slot (Wh = W × 0,25 h)
    v_pv_b_production_wh := NULL;
    FOR r_bonus IN
        SELECT id
        FROM ems.asset_pv_array
        WHERE site_id = p_site_id
          AND green_bonus_czk_kwh IS NOT NULL
    LOOP
        SELECT fpi.power_w * 0.25
        INTO v_array_prod_wh
        FROM ems.forecast_pv_interval fpi
        JOIN ems.forecast_pv_run fpr ON fpr.id = fpi.run_id
        WHERE fpr.site_id = p_site_id
          AND fpr.pv_array_id = r_bonus.id
          AND fpi.interval_start = p_interval_start
          AND fpr.status = 'ok'
        ORDER BY fpr.created_at DESC
        LIMIT 1;

        v_array_prod_wh := COALESCE(v_array_prod_wh, 0);
        IF v_pv_b_production_wh IS NULL THEN
            v_pv_b_production_wh := 0;
        END IF;
        v_pv_b_production_wh := v_pv_b_production_wh + v_array_prod_wh;
        v_green_bonus_czk := v_green_bonus_czk + ems.fn_green_bonus_revenue(
            r_bonus.id,
            p_interval_start,
            v_array_prod_wh
        );
    END LOOP;

    -- Upsert do audit_interval
    INSERT INTO ems.audit_interval (
        site_id, interval_start, planning_run_id,
        actual_pv_power_w, actual_battery_power_w,
        actual_grid_power_w, actual_load_power_w,
        actual_battery_soc_pct,
        actual_ev_power_w,
        actual_heat_pump_power_w,
        actual_cost_czk,
        pv_b_production_wh,
        green_bonus_czk,
        deviation_grid_w,
        deviation_cost_czk
    ) VALUES (
        p_site_id, p_interval_start, v_run_id,
        v_avg_pv_power_w,
        v_avg_battery_power_w,
        v_avg_grid_power_w,
        v_avg_load_power_w,
        v_last_soc,
        v_sum_ev_power_w,
        v_avg_hp_power_w,
        ROUND(v_actual_cost, 4),
        v_pv_b_production_wh,
        ROUND(v_green_bonus_czk, 4),
        CASE WHEN v_plan.run_id IS NOT NULL
             THEN v_avg_grid_power_w - v_plan.grid_setpoint_w
             ELSE NULL END,
        CASE WHEN v_plan.run_id IS NOT NULL
             THEN ROUND(v_actual_cost - COALESCE(v_plan.expected_cost_czk, 0), 4)
             ELSE NULL END
    )
    ON CONFLICT (site_id, interval_start) DO UPDATE SET
        planning_run_id          = EXCLUDED.planning_run_id,
        actual_pv_power_w        = EXCLUDED.actual_pv_power_w,
        actual_battery_power_w   = EXCLUDED.actual_battery_power_w,
        actual_grid_power_w      = EXCLUDED.actual_grid_power_w,
        actual_load_power_w      = EXCLUDED.actual_load_power_w,
        actual_battery_soc_pct   = EXCLUDED.actual_battery_soc_pct,
        actual_ev_power_w        = EXCLUDED.actual_ev_power_w,
        actual_heat_pump_power_w = EXCLUDED.actual_heat_pump_power_w,
        actual_cost_czk          = EXCLUDED.actual_cost_czk,
        pv_b_production_wh       = EXCLUDED.pv_b_production_wh,
        green_bonus_czk          = EXCLUDED.green_bonus_czk,
        deviation_grid_w         = EXCLUDED.deviation_grid_w,
        deviation_cost_czk       = EXCLUDED.deviation_cost_czk;
END;
$$;

COMMENT ON FUNCTION ems.fn_fill_audit_interval(INT, TIMESTAMPTZ) IS
'Naplní nebo aktualizuje jeden řádek v audit_interval pro danou lokalitu a 15min interval.
Agreguje průměry z telemetrie (střídač, EV, TČ), porovná se skutečným plánem a spočítá odchylky.
Zelený bonus: součet přes všechna pole s nastaveným bonusem, výroba z poslední ok forecast_pv_interval.
Volat každých 15 minut pro interval který právě skončil.';

-- ============================================================
-- Hromadné plnění auditu za historické období
-- ============================================================

CREATE OR REPLACE FUNCTION ems.fn_fill_audit_range(
    p_site_id    INT,
    p_from       TIMESTAMPTZ,
    p_to         TIMESTAMPTZ
)
RETURNS INT
LANGUAGE plpgsql
AS $$
DECLARE
    v_slot  TIMESTAMPTZ;
    v_count INT := 0;
BEGIN
    v_slot := date_trunc('hour', p_from)
              + INTERVAL '15 min' * FLOOR(EXTRACT(MINUTE FROM p_from) / 15);

    WHILE v_slot < p_to LOOP
        PERFORM ems.fn_fill_audit_interval(p_site_id, v_slot);
        v_slot  := v_slot + INTERVAL '15 minutes';
        v_count := v_count + 1;
    END LOOP;

    RETURN v_count;
END;
$$;

COMMENT ON FUNCTION ems.fn_fill_audit_range(INT, TIMESTAMPTZ, TIMESTAMPTZ) IS
'Hromadně naplní audit_interval pro celé historické období.
Volá fn_fill_audit_interval pro každý 15min slot v rozsahu p_from–p_to.
Vrátí počet zpracovaných intervalů. Použít pro backfill po výpadku nebo prvním nasazení.';
