-- =============================================================
-- R__011_fn_effective_price.sql
-- EMS Platform – funkce pro výpočet efektivní ceny per site
-- Repeatable migration – nasazuje se při každé změně
-- =============================================================
-- Pomocné (audit/ekonomika): sjednocení import/export Wh — musí běžet před R__012/R__056.
create or replace function ems.fn_audit_grid_import_wh_for_economics(
  p_import_wh numeric,
  p_export_wh numeric,
  p_grid_power_w int
)
returns numeric
language sql
stable
as $$
  select case
    when coalesce(p_import_wh, 0) > 0 and coalesce(p_export_wh, 0) > 0 then
      coalesce(
        p_import_wh,
        greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
      )
    when coalesce(p_export_wh, 0) = 0 then
      greatest(
        coalesce(
          p_import_wh,
          greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
        ),
        greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
      )
    else
      coalesce(
        p_import_wh,
        greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
      )
  end;
$$;

create or replace function ems.fn_audit_grid_export_wh_for_economics(
  p_import_wh numeric,
  p_export_wh numeric,
  p_grid_power_w int
)
returns numeric
language sql
stable
as $$
  select case
    when coalesce(p_import_wh, 0) > 0 and coalesce(p_export_wh, 0) > 0 then
      coalesce(
        p_export_wh,
        abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
      )
    when coalesce(p_import_wh, 0) = 0 then
      greatest(
        coalesce(
          p_export_wh,
          abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
        ),
        abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
      )
    else
      coalesce(
        p_export_wh,
        abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
      )
  end;
$$;

comment on function ems.fn_audit_grid_import_wh_for_economics(numeric, numeric, int) is
  'Import Wh pro audit/ekonomiku: u čistého importu max(uložený čítač, max(0,P_grid)×¼ h).';

comment on function ems.fn_audit_grid_export_wh_for_economics(numeric, numeric, int) is
  'Export Wh pro audit/ekonomiku: u čistého exportu max(uložený čítač, |min(0,P_grid)|×¼ h).';

CREATE OR REPLACE FUNCTION ems.fn_effective_buy_price(
    p_site_id        INT,
    p_interval_start TIMESTAMPTZ
)
RETURNS NUMERIC(10,6)
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
    v_spot_price       NUMERIC;
    v_energy_czk       NUMERIC;
    v_dist_rate        NUMERIC;
    v_system_services  NUMERIC;
    v_ote_fee          NUMERIC;
    v_vat_rate         NUMERIC;
    v_buy_margin_fixed NUMERIC;
    v_buy_margin_pct   NUMERIC;
    v_buy_margin       NUMERIC;
    v_is_vt            BOOLEAN;
    v_local_time       TIME;
    v_dow              INT;
    v_hdo_code_id      INT;
    v_tariff_id        INT;
    v_rate_type        TEXT;
    v_purchase_mode    TEXT;
    v_fixed_nt         NUMERIC;
    v_fixed_vt_sur     NUMERIC;
BEGIN
    SELECT
        smc.purchase_pricing_mode,
        smc.buy_fixed_energy_nt_czk_kwh,
        smc.buy_fixed_vt_surcharge_czk_kwh,
        smc.buy_margin_fixed_czk,
        smc.buy_margin_percent,
        smc.system_services_czk_kwh,
        smc.ote_fee_czk_kwh,
        smc.hdo_code_id,
        smc.tariff_id,
        dt.vat_rate
    INTO
        v_purchase_mode,
        v_fixed_nt,
        v_fixed_vt_sur,
        v_buy_margin_fixed,
        v_buy_margin_pct,
        v_system_services,
        v_ote_fee,
        v_hdo_code_id,
        v_tariff_id,
        v_vat_rate
    FROM ems.site_market_config smc
    LEFT JOIN ems.distribution_tariff dt ON dt.id = smc.tariff_id
    WHERE smc.site_id = p_site_id
      AND smc.valid_from <= p_interval_start
      AND (smc.valid_to IS NULL OR smc.valid_to > p_interval_start)
    ORDER BY smc.valid_from DESC
    LIMIT 1;

    IF NOT FOUND THEN
        RETURN NULL;
    END IF;

    SELECT buy_raw_price_czk_kwh INTO v_spot_price
    FROM ems.market_interval_price
    WHERE market_source IN ('OTE_CZ', 'OTE_CZ_DAM')
      AND interval_start = p_interval_start
    LIMIT 1;

    v_local_time := (p_interval_start AT TIME ZONE 'Europe/Prague')::TIME;
    v_dow        := EXTRACT(DOW FROM p_interval_start AT TIME ZONE 'Europe/Prague');
    -- 0=neděle, 6=sobota

    IF v_hdo_code_id IS NOT NULL THEN
        SELECT EXISTS (
            SELECT 1
            FROM ems.hdo_code_window w
            WHERE w.hdo_code_id = v_hdo_code_id
              AND (
                w.day_type = 'all'
                OR (w.day_type = 'workday' AND v_dow BETWEEN 1 AND 5)
                OR (w.day_type = 'weekend' AND v_dow IN (0, 6))
              )
              AND w.rate_type = 'VT'
              AND v_local_time >= w.window_from
              AND v_local_time <  w.window_to
        ) INTO v_is_vt;
    ELSE
        v_is_vt := false;
    END IF;

    v_rate_type := CASE WHEN v_is_vt THEN 'VT' ELSE 'NT' END;

    IF v_tariff_id IS NOT NULL THEN
        SELECT price_czk_kwh INTO v_dist_rate
        FROM ems.distribution_tariff_rate
        WHERE tariff_id  = v_tariff_id
          AND rate_type  = v_rate_type
          AND valid_from <= p_interval_start::DATE
          AND (valid_to IS NULL OR valid_to > p_interval_start::DATE)
        ORDER BY valid_from DESC
        LIMIT 1;
    END IF;

    v_dist_rate       := COALESCE(v_dist_rate, 0);
    v_system_services := COALESCE(v_system_services, 0);
    v_ote_fee         := COALESCE(v_ote_fee, 0);
    v_buy_margin_fixed := COALESCE(v_buy_margin_fixed, 0);
    v_buy_margin_pct   := COALESCE(v_buy_margin_pct, 0);
    v_vat_rate        := COALESCE(v_vat_rate, 0.21);
    v_fixed_vt_sur    := COALESCE(v_fixed_vt_sur, 0);

    IF upper(trim(COALESCE(v_purchase_mode, ''))) = 'FIXED'
       AND v_fixed_nt IS NOT NULL THEN
        v_energy_czk := v_fixed_nt
            + CASE WHEN v_is_vt THEN v_fixed_vt_sur ELSE 0 END;
        v_buy_margin := v_buy_margin_fixed + (v_energy_czk * v_buy_margin_pct / 100.0);
    ELSIF v_spot_price IS NULL THEN
        RETURN NULL;
    ELSE
        -- Spot: asymetrický faktor na raw OTE (stejné p jako u kladného ×(1+p/100)):
        --  kladná raw → ×(1+p/100), záporná raw → ×(1−p/100); fixní marže jen přičíst.
        v_energy_czk := CASE
            WHEN v_spot_price >= 0 THEN
                v_spot_price * (1 + v_buy_margin_pct / 100.0)
            ELSE
                v_spot_price * (1 - v_buy_margin_pct / 100.0)
        END;
        v_buy_margin := v_buy_margin_fixed;
    END IF;

    RETURN ROUND(
        (v_energy_czk + v_dist_rate + v_system_services + v_ote_fee + v_buy_margin)
        * (1 + v_vat_rate),
        6
    );
END;
$$;

COMMENT ON FUNCTION ems.fn_effective_buy_price(INT, TIMESTAMPTZ) IS
'Efektivní nákupní cena elektřiny Kč/kWh včetně DPH.
Režim spot: složka OTE buy_raw jako kladná → ×(1+buy_margin_percent/100), záporná → ×(1−buy_margin_percent/100); + buy_margin_fixed_czk + distribuce NT/VT (HDO) + systémové služby + OTE poplatek; pak DPH na celek.
Režim fixed: energie = buy_fixed_energy_nt_czk_kwh (+ příplatek VT dle HDO), marže = fix + procento z této uzavřené energické složky (symetricky jako dříve); + příplatky a DPH stejně.
DPH aplikováno na celou částku.';

-- ------------------------------------------------------------

CREATE OR REPLACE FUNCTION ems.fn_effective_sell_price(
    p_site_id        INT,
    p_interval_start TIMESTAMPTZ
)
RETURNS NUMERIC(10,6)
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
    v_spot_price         NUMERIC;
    v_sell_margin_fixed  NUMERIC;
    v_sell_margin_pct    NUMERIC;
BEGIN
    SELECT sell_margin_fixed_czk, sell_margin_percent
    INTO v_sell_margin_fixed, v_sell_margin_pct
    FROM ems.site_market_config
    WHERE site_id = p_site_id
      AND valid_from <= p_interval_start
      AND (valid_to IS NULL OR valid_to > p_interval_start)
    ORDER BY valid_from DESC
    LIMIT 1;

    IF NOT FOUND THEN
        RETURN NULL;
    END IF;

    SELECT sell_raw_price_czk_kwh INTO v_spot_price
    FROM ems.market_interval_price
    WHERE market_source IN ('OTE_CZ', 'OTE_CZ_DAM')
      AND interval_start = p_interval_start
    LIMIT 1;

    IF v_spot_price IS NULL THEN
        RETURN NULL;
    END IF;

    RETURN ROUND(
        v_spot_price
        + COALESCE(v_sell_margin_fixed, 0)
        + (v_spot_price * COALESCE(v_sell_margin_pct, 0) / 100.0),
        6
    );
END;
$$;

COMMENT ON FUNCTION ems.fn_effective_sell_price(INT, TIMESTAMPTZ) IS
'Efektivní prodejní cena elektřiny Kč/kWh bez DPH (neplátce DPH).
Složky: spot OTE + fixní/procentní prodejní marže (záporná = srážka).
Zelený bonus není součástí ceny – počítá se z výroby přes fn_green_bonus_revenue().
Záporná hodnota = platíme za export (záporné spotové ceny).';

-- ------------------------------------------------------------

CREATE OR REPLACE FUNCTION ems.fn_green_bonus_revenue(
    p_pv_array_id    INT,
    p_interval_start TIMESTAMPTZ,
    p_production_wh  NUMERIC
)
RETURNS NUMERIC
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
    v_bonus_rate NUMERIC;
BEGIN
    SELECT green_bonus_czk_kwh INTO v_bonus_rate
    FROM ems.asset_pv_array
    WHERE id = p_pv_array_id
      AND green_bonus_czk_kwh IS NOT NULL
      AND green_bonus_valid_from <= p_interval_start::DATE
      AND (green_bonus_valid_to IS NULL
           OR green_bonus_valid_to > p_interval_start::DATE);

    IF v_bonus_rate IS NULL OR p_production_wh IS NULL OR p_production_wh <= 0 THEN
        RETURN 0;
    END IF;

    RETURN ROUND((p_production_wh / 1000.0) * v_bonus_rate, 6);
END;
$$;

COMMENT ON FUNCTION ems.fn_green_bonus_revenue(INT, TIMESTAMPTZ, NUMERIC) IS
'Příjem ze zeleného bonusu za výrobu FVE pole v daném intervalu.
Bonus plyne z celkové výroby bez ohledu na to kam energie šla
(interní spotřeba, baterie, EV, TČ i export do sítě).
Sazba se načítá dle platnosti (valid_from/valid_to) – ročně aktualizovatelné.
Vrátí 0 pokud pole nemá zelený bonus nebo výroba je nulová.
Použití: SELECT ems.fn_green_bonus_revenue(pv_array_id, interval_start, production_wh);';
