-- =============================================================
-- R__019_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;

    -- per-minute Wh veličiny
    v_grid_import_wh       NUMERIC;
    v_grid_export_wh       NUMERIC;
    v_batt_charge_wh       NUMERIC;
    v_batt_discharge_wh    NUMERIC;
    v_pv_production_wh     NUMERIC;
    v_load_consumption_wh  NUMERIC;

    -- Deye counter delta
    v_counter_import_first BIGINT;
    v_counter_import_last  BIGINT;
    v_counter_export_first BIGINT;
    v_counter_export_last  BIGINT;
    v_delta_import         NUMERIC;
    v_delta_export         NUMERIC;
    v_imp_before           NUMERIC;
    v_exp_before           NUMERIC;

    -- 7 směrových toků (prioritní alokace per minuta; součet W/60 = Wh)
    r_flow                 RECORD;
    v_flow_samples         INT := 0;
    v_acc_ptl              NUMERIC := 0;
    v_acc_ptb              NUMERIC := 0;
    v_acc_ptg              NUMERIC := 0;
    v_acc_btl              NUMERIC := 0;
    v_acc_btg              NUMERIC := 0;
    v_acc_gtl              NUMERIC := 0;
    v_acc_gtb              NUMERIC := 0;
    v_pv                   NUMERIC;
    v_load_m               NUMERIC;
    v_gi                   NUMERIC;
    v_ge                   NUMERIC;
    v_bc                   NUMERIC;
    v_bd                   NUMERIC;
    v_ptl                  NUMERIC;
    v_ptb                  NUMERIC;
    v_ptg                  NUMERIC;
    v_btl                  NUMERIC;
    v_btg                  NUMERIC;
    v_gtl                  NUMERIC;
    v_gtb                  NUMERIC;
    v_flow_pv_to_load_wh   NUMERIC;
    v_flow_pv_to_batt_wh   NUMERIC;
    v_flow_pv_to_grid_wh   NUMERIC;
    v_flow_batt_to_load_wh NUMERIC;
    v_flow_batt_to_grid_wh NUMERIC;
    v_flow_grid_to_load_wh NUMERIC;
    v_flow_grid_to_batt_wh NUMERIC;
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ěry (pro zpětnou kompatibilitu) + per-minute split pro Wh
    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),
        -- Per-minute split: každý vzorek × 1/60 h = Wh
        ROUND(SUM(GREATEST(grid_power_w, 0))::NUMERIC / 60, 1),
        ROUND(SUM(ABS(LEAST(grid_power_w, 0)))::NUMERIC / 60, 1),
        ROUND(SUM(ABS(LEAST(battery_power_w, 0)))::NUMERIC / 60, 1),
        ROUND(SUM(GREATEST(battery_power_w, 0))::NUMERIC / 60, 1),
        ROUND(SUM(GREATEST(pv_power_w, 0))::NUMERIC / 60, 1),
        ROUND(SUM(GREATEST(load_power_w, 0))::NUMERIC / 60, 1),
        -- Deye total energy counter delta
        FIRST(grid_import_total_wh, measured_at),
        LAST(grid_import_total_wh, measured_at),
        FIRST(grid_export_total_wh, measured_at),
        LAST(grid_export_total_wh, 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,
        v_grid_import_wh,
        v_grid_export_wh,
        v_batt_charge_wh,
        v_batt_discharge_wh,
        v_pv_production_wh,
        v_load_consumption_wh,
        v_counter_import_first,
        v_counter_import_last,
        v_counter_export_first,
        v_counter_export_last
    FROM ems.telemetry_inverter
    WHERE site_id = p_site_id
      AND measured_at >= p_interval_start
      AND measured_at < v_interval_end;

    -- Deye counter delta (primární zdroj pro grid import/export, pokud jsou čítače dostupné)
    IF v_counter_import_first IS NOT NULL AND v_counter_import_last IS NOT NULL
       AND v_counter_import_last >= v_counter_import_first THEN
        v_delta_import := v_counter_import_last - v_counter_import_first;
        v_grid_import_wh := v_delta_import;
    END IF;
    IF v_counter_export_first IS NOT NULL AND v_counter_export_last IS NOT NULL
       AND v_counter_export_last >= v_counter_export_first THEN
        v_delta_export := v_counter_export_last - v_counter_export_first;
        v_grid_export_wh := v_delta_export;
    END IF;

    v_imp_before := v_grid_import_wh;
    v_exp_before := v_grid_export_wh;
    v_grid_import_wh := ems.fn_audit_grid_import_wh_for_economics(
        v_imp_before, v_exp_before, v_avg_grid_power_w);
    v_grid_export_wh := ems.fn_audit_grid_export_wh_for_economics(
        v_imp_before, v_exp_before, v_avg_grid_power_w);

    -- Agregovat EV nabíječky: sum(W·min) / 15 = průměrný výkon slotu.
    -- Telemetrie je idle-skip (telemetry_collector): vypnutá nabíječka má jen
    -- heartbeat řádky — avg přes přítomné řádky by nabíjení části slotu
    -- NADHODNOTILO; chybějící minuta = 0 W (zařízení idle).
    select round(coalesce(sum(power_w), 0) / 15.0)::int
    into v_sum_ev_power_w
    from ems.telemetry_ev_charger
    where site_id = p_site_id
      and measured_at >= p_interval_start
      and measured_at < v_interval_end;

    -- Agregovat tepelné čerpadlo: stejný sumový přístup (idle-skip řádky);
    -- NULL pokud žádný řádek nenese power_w (MIM-B19N příkon neměří).
    select case
        when count(power_w) = 0 then null
        else round(sum(power_w) / 15.0)::int
      end
    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 per-direction (import × buy - export × sell)
    IF v_grid_import_wh IS NOT NULL OR v_grid_export_wh IS NOT NULL THEN
        v_actual_cost := COALESCE(v_grid_import_wh, 0) / 1000.0 * COALESCE(v_buy_price, 0)
                       - COALESCE(v_grid_export_wh, 0) / 1000.0 * COALESCE(v_sell_price, 0);
    END IF;

    -- Zelený bonus: výroba bonusových polí z reálné telemetrie (Wh = průměr W × 0,25 h)
    v_pv_b_production_wh := NULL;
    FOR r_bonus IN
        SELECT pa.id, pa.inverter_id, pa.telemetry_source
        FROM ems.asset_pv_array pa
        WHERE pa.site_id = p_site_id
          AND pa.green_bonus_czk_kwh IS NOT NULL
          AND pa.green_bonus_valid_from <= p_interval_start::DATE
          AND (pa.green_bonus_valid_to IS NULL
               OR pa.green_bonus_valid_to > p_interval_start::DATE)
    LOOP
        v_array_prod_wh := NULL;

        IF r_bonus.telemetry_source IS NOT NULL AND r_bonus.inverter_id IS NOT NULL THEN
            SELECT AVG(
                CASE r_bonus.telemetry_source
                    WHEN 'gen_port'   THEN ti.gen_port_power_w
                    WHEN 'pv1'        THEN ti.pv1_power_w
                    WHEN 'pv2'        THEN ti.pv2_power_w
                    WHEN 'pv_strings' THEN COALESCE(ti.pv1_power_w, 0)
                                           + COALESCE(ti.pv2_power_w, 0)
                    WHEN 'pv_total'   THEN ti.pv_power_w
                    ELSE NULL
                END
            )::NUMERIC * 0.25
            INTO v_array_prod_wh
            FROM ems.telemetry_inverter ti
            WHERE ti.inverter_id = r_bonus.inverter_id
              AND ti.measured_at >= p_interval_start
              AND ti.measured_at < v_interval_end;
        END IF;

        IF v_array_prod_wh IS NULL THEN
            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;
        END IF;

        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;

    -- Prioritní alokace toků: PV → load → batt charge → export; pak batt discharge → load/export; grid → zbytek
    FOR r_flow IN
        SELECT pv_power_w, grid_power_w, battery_power_w, load_power_w
        FROM ems.telemetry_inverter
        WHERE site_id = p_site_id
          AND measured_at >= p_interval_start
          AND measured_at < v_interval_end
        ORDER BY measured_at
    LOOP
        v_flow_samples := v_flow_samples + 1;
        v_pv   := GREATEST(COALESCE(r_flow.pv_power_w, 0)::NUMERIC, 0);
        v_load_m := GREATEST(COALESCE(r_flow.load_power_w, 0)::NUMERIC, 0);
        v_gi   := GREATEST(COALESCE(r_flow.grid_power_w, 0)::NUMERIC, 0);
        v_ge   := ABS(LEAST(COALESCE(r_flow.grid_power_w, 0)::NUMERIC, 0));
        v_bc   := ABS(LEAST(COALESCE(r_flow.battery_power_w, 0)::NUMERIC, 0));
        v_bd   := GREATEST(COALESCE(r_flow.battery_power_w, 0)::NUMERIC, 0);

        v_ptl := LEAST(v_pv, v_load_m);
        v_ptb := LEAST(v_pv - v_ptl, v_bc);
        v_ptg := LEAST(v_pv - v_ptl - v_ptb, v_ge);
        v_btl := LEAST(v_bd, v_load_m - v_ptl);
        v_btg := LEAST(v_bd - v_btl, GREATEST(0::NUMERIC, v_ge - v_ptg));
        v_gtl := GREATEST(0::NUMERIC, v_load_m - v_ptl - v_btl);
        v_gtb := GREATEST(0::NUMERIC, v_bc - v_ptb);

        v_acc_ptl := v_acc_ptl + v_ptl;
        v_acc_ptb := v_acc_ptb + v_ptb;
        v_acc_ptg := v_acc_ptg + v_ptg;
        v_acc_btl := v_acc_btl + v_btl;
        v_acc_btg := v_acc_btg + v_btg;
        v_acc_gtl := v_acc_gtl + v_gtl;
        v_acc_gtb := v_acc_gtb + v_gtb;
    END LOOP;

    IF v_flow_samples = 0 THEN
        v_flow_pv_to_load_wh   := NULL;
        v_flow_pv_to_batt_wh   := NULL;
        v_flow_pv_to_grid_wh   := NULL;
        v_flow_batt_to_load_wh := NULL;
        v_flow_batt_to_grid_wh := NULL;
        v_flow_grid_to_load_wh := NULL;
        v_flow_grid_to_batt_wh := NULL;
    ELSE
        v_flow_pv_to_load_wh   := ROUND(v_acc_ptl / 60, 1);
        v_flow_pv_to_batt_wh   := ROUND(v_acc_ptb / 60, 1);
        v_flow_pv_to_grid_wh   := ROUND(v_acc_ptg / 60, 1);
        v_flow_batt_to_load_wh := ROUND(v_acc_btl / 60, 1);
        v_flow_batt_to_grid_wh := ROUND(v_acc_btg / 60, 1);
        v_flow_grid_to_load_wh := ROUND(v_acc_gtl / 60, 1);
        v_flow_grid_to_batt_wh := ROUND(v_acc_gtb / 60, 1);
    END IF;

    -- 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,
        actual_grid_import_wh,
        actual_grid_export_wh,
        actual_batt_charge_wh,
        actual_batt_discharge_wh,
        actual_pv_production_wh,
        actual_load_consumption_wh,
        flow_pv_to_load_wh,
        flow_pv_to_batt_wh,
        flow_pv_to_grid_wh,
        flow_batt_to_load_wh,
        flow_batt_to_grid_wh,
        flow_grid_to_load_wh,
        flow_grid_to_batt_wh
    ) 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,
        v_grid_import_wh,
        v_grid_export_wh,
        v_batt_charge_wh,
        v_batt_discharge_wh,
        v_pv_production_wh,
        v_load_consumption_wh,
        v_flow_pv_to_load_wh,
        v_flow_pv_to_batt_wh,
        v_flow_pv_to_grid_wh,
        v_flow_batt_to_load_wh,
        v_flow_batt_to_grid_wh,
        v_flow_grid_to_load_wh,
        v_flow_grid_to_batt_wh
    )
    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,
        actual_grid_import_wh    = EXCLUDED.actual_grid_import_wh,
        actual_grid_export_wh    = EXCLUDED.actual_grid_export_wh,
        actual_batt_charge_wh    = EXCLUDED.actual_batt_charge_wh,
        actual_batt_discharge_wh = EXCLUDED.actual_batt_discharge_wh,
        actual_pv_production_wh  = EXCLUDED.actual_pv_production_wh,
        actual_load_consumption_wh = EXCLUDED.actual_load_consumption_wh,
        flow_pv_to_load_wh         = EXCLUDED.flow_pv_to_load_wh,
        flow_pv_to_batt_wh         = EXCLUDED.flow_pv_to_batt_wh,
        flow_pv_to_grid_wh         = EXCLUDED.flow_pv_to_grid_wh,
        flow_batt_to_load_wh       = EXCLUDED.flow_batt_to_load_wh,
        flow_batt_to_grid_wh       = EXCLUDED.flow_batt_to_grid_wh,
        flow_grid_to_load_wh       = EXCLUDED.flow_grid_to_load_wh,
        flow_grid_to_batt_wh       = EXCLUDED.flow_grid_to_batt_wh;
END;
$$;

COMMENT ON FUNCTION ems.fn_fill_audit_interval IS
'Naplní nebo aktualizuje jeden řádek v audit_interval pro danou lokalitu a 15min interval.
Agreguje telemetrii (střídač průměry; EV a TČ sum/15 — idle-skip zápisy, chybějící minuta = 0 W),
porovná se skutečným plánem a spočítá odchylky.
Per-minutový split pro 6 energetických veličin (import/export/batt/PV/load Wh);
grid import/export nejprve z delta Deye total counterů (reg 522-525), fallback per-minute; poté sjednocení
fn_audit_grid_*_wh_for_economics (u jednosměrného toku max s odhadem z průměrného grid_power_w).
7 směrových toků (flow_*_wh): prioritní alokace per minuta z telemetrie (PV→load→batt→export; baterie→load/export; síť→zbytek).
actual_cost_czk = per-direction (import_wh × buy - export_wh × sell).
Zelený bonus: součet přes pole s green_bonus_czk_kwh.
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í.';
