-- Denní ekonomika za [p_ts_from, p_ts_to): jen audit_interval v okně + joiny (žádný vw_site_effective_price,
-- žádné volání fn_effective_* per řádek — musí zůstat v souladu s R__011_fn_effective_price.sql).

create or replace function ems.fn_economics_daily_for_window(
  p_site_id int,
  p_ts_from timestamptz,
  p_ts_to timestamptz
)
returns table (
  site_id int,
  day_local date,
  interval_count int,
  import_kwh numeric,
  export_kwh numeric,
  pv_kwh numeric,
  load_kwh numeric,
  pv_self_consumption_kwh numeric,
  ev_kwh numeric,
  hp_kwh numeric,
  grid_import_cashflow_czk numeric,
  grid_export_revenue_czk numeric,
  import_cost_czk numeric,
  export_revenue_czk numeric,
  net_cost_czk numeric,
  green_bonus_czk numeric,
  total_balance_czk numeric,
  planned_net_cost_czk numeric,
  planned_balance_czk numeric,
  deviation_cost_czk numeric
)
language sql
stable
as $fn$
  with slot0 as (
    select
      ai.site_id,
      ai.interval_start,
      (date_trunc('day', ai.interval_start at time zone 'Europe/Prague'))::date as day_local,
      ai.actual_grid_import_wh,
      ai.actual_grid_power_w,
      ai.actual_grid_export_wh,
      ai.green_bonus_czk,
      ai.actual_pv_power_w,
      ai.actual_load_power_w,
      ai.actual_ev_power_w,
      ai.actual_heat_pump_power_w,
      smc.id as market_config_id,
      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.sell_margin_fixed_czk,
      smc.sell_margin_percent,
      smc.system_services_czk_kwh,
      smc.ote_fee_czk_kwh,
      smc.hdo_code_id,
      smc.tariff_id,
      coalesce(dt.vat_rate, 0.21) as vat_rate,
      mip.buy_raw_price_czk_kwh as buy_spot,
      mip.sell_raw_price_czk_kwh as sell_spot,
      pi.expected_cost_czk as planned_cost_czk,
      ems.fn_audit_grid_import_wh_for_economics(
        ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
      ) as est_grid_import_wh,
      ems.fn_audit_grid_export_wh_for_economics(
        ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
      ) as est_grid_export_wh
    from ems.audit_interval ai
    left join lateral (
      select smc_inner.*
      from ems.site_market_config smc_inner
      where smc_inner.site_id = ai.site_id
        and smc_inner.valid_from <= ai.interval_start
        and (smc_inner.valid_to is null or smc_inner.valid_to > ai.interval_start)
      order by smc_inner.valid_from desc
      limit 1
    ) smc on true
    left join ems.distribution_tariff dt on dt.id = smc.tariff_id
    left join lateral (
      select
        mip_inner.buy_raw_price_czk_kwh,
        mip_inner.sell_raw_price_czk_kwh
      from ems.market_interval_price mip_inner
      where mip_inner.interval_start = ai.interval_start
        and mip_inner.market_source in ('OTE_CZ', 'OTE_CZ_DAM')
      limit 1
    ) mip on true
    left join ems.planning_interval pi
      on pi.run_id = ai.planning_run_id
     and pi.interval_start = ai.interval_start
    where ai.site_id = p_site_id
      and ai.interval_start >= p_ts_from
      and ai.interval_start < p_ts_to
  ),
  slot1 as (
    select
      s0.*,
      case
        when s0.hdo_code_id is null then false
        else exists (
          select 1
          from ems.hdo_code_window w
          where w.hdo_code_id = s0.hdo_code_id
            and (
              w.day_type = 'all'
              or (
                w.day_type = 'workday'
                and extract(dow from s0.interval_start at time zone 'Europe/Prague') between 1 and 5
              )
              or (
                w.day_type = 'weekend'
                and extract(dow from s0.interval_start at time zone 'Europe/Prague') in (0, 6)
              )
            )
            and w.rate_type = 'VT'
            and (s0.interval_start at time zone 'Europe/Prague')::time >= w.window_from
            and (s0.interval_start at time zone 'Europe/Prague')::time < w.window_to
        )
      end as is_vt
    from slot0 s0
  ),
  slot2 as (
    select
      s1.*,
      dr.dist_rate
    from slot1 s1
    left join lateral (
      select dtr.price_czk_kwh as dist_rate
      from ems.distribution_tariff_rate dtr
      where dtr.tariff_id = s1.tariff_id
        and dtr.rate_type = case when s1.is_vt then 'VT'::text else 'NT'::text end
        and dtr.valid_from <= s1.interval_start::date
        and (dtr.valid_to is null or dtr.valid_to > s1.interval_start::date)
      order by dtr.valid_from desc
      limit 1
    ) dr on s1.tariff_id is not null
  ),
  slot3 as (
    select
      s2.*,
      case
        when upper(trim(coalesce(s2.purchase_pricing_mode, ''))) = 'FIXED'
          and s2.buy_fixed_energy_nt_czk_kwh is not null
        then
          s2.buy_fixed_energy_nt_czk_kwh
          + case
              when s2.is_vt then coalesce(s2.buy_fixed_vt_surcharge_czk_kwh, 0)
              else 0::numeric
            end
        when s2.buy_spot is null then null::numeric
        else s2.buy_spot
      end as energy_czk
    from slot2 s2
  ),
  slot4 as (
    select
      s3.*,
      case
        when s3.market_config_id is null then null::numeric
        when s3.energy_czk is null then null::numeric
        when upper(trim(coalesce(s3.purchase_pricing_mode, ''))) = 'FIXED'
          and s3.buy_fixed_energy_nt_czk_kwh is not null
        then
          s3.energy_czk
          + coalesce(s3.buy_margin_fixed_czk, 0)
          + (s3.energy_czk * coalesce(s3.buy_margin_percent, 0) / 100.0)
        else
          case
            when s3.buy_spot >= 0 then
              s3.buy_spot * (1 + coalesce(s3.buy_margin_percent, 0) / 100.0)
            else
              s3.buy_spot * (1 - coalesce(s3.buy_margin_percent, 0) / 100.0)
          end
          + coalesce(s3.buy_margin_fixed_czk, 0)
      end as buy_pre_dist_margin_sum
    from slot3 s3
  ),
  slot5 as (
    select
      s4.*,
      case
        when s4.market_config_id is null then null::numeric
        when s4.energy_czk is null then null::numeric
        else round(
          (
            s4.buy_pre_dist_margin_sum
            + coalesce(s4.dist_rate, 0)
            + coalesce(s4.system_services_czk_kwh, 0)
            + coalesce(s4.ote_fee_czk_kwh, 0)
          ) * (1 + coalesce(s4.vat_rate, 0.21)),
          6
        )
      end as buy_p,
      case
        when s4.market_config_id is null then null::numeric
        when s4.sell_spot is null then null::numeric
        else round(
          s4.sell_spot
          + coalesce(s4.sell_margin_fixed_czk, 0)
          + (s4.sell_spot * coalesce(s4.sell_margin_percent, 0) / 100.0),
          6
        )
      end as sell_p
    from slot4 s4
  ),
  slots as (
    select
      s5.site_id,
      s5.day_local,
      round(s5.est_grid_import_wh / 1000, 4) as import_kwh,
      round(s5.est_grid_export_wh / 1000, 4) as export_kwh,
      round(
        s5.est_grid_import_wh / 1000.0 * coalesce(s5.buy_p, 0),
        4
      ) as grid_import_cashflow_czk,
      round(
        s5.est_grid_export_wh / 1000.0 * coalesce(s5.sell_p, 0),
        4
      ) as grid_export_revenue_czk,
      round(
        s5.est_grid_import_wh / 1000.0 * coalesce(s5.buy_p, 0)
        - s5.est_grid_export_wh / 1000.0 * coalesce(s5.sell_p, 0),
        4
      ) as dynamic_cost_czk,
      s5.green_bonus_czk,
      s5.planned_cost_czk,
      s5.actual_pv_power_w,
      s5.actual_load_power_w,
      s5.actual_ev_power_w,
      s5.actual_heat_pump_power_w
    from slot5 s5
  ),
  daily_agg as (
    select
      s.site_id,
      s.day_local,
      count(*)::int as interval_count,
      round(sum(s.import_kwh), 3) as import_kwh,
      round(sum(s.export_kwh), 3) as export_kwh,
      round(sum(greatest(s.actual_pv_power_w, 0)::numeric / 4000), 3) as pv_kwh,
      round(sum(greatest(s.actual_load_power_w, 0)::numeric / 4000), 3) as load_kwh,
      round(sum(greatest(s.actual_ev_power_w, 0)::numeric / 4000), 3) as ev_kwh,
      round(sum(greatest(s.actual_heat_pump_power_w, 0)::numeric / 4000), 3) as hp_kwh,
      round(
        sum(greatest(s.actual_pv_power_w, 0)::numeric / 4000) - sum(s.export_kwh),
        3
      ) as pv_self_consumption_kwh,
      round(sum(s.grid_import_cashflow_czk), 2) as grid_import_cashflow_czk,
      round(sum(s.grid_export_revenue_czk), 2) as grid_export_revenue_czk,
      round(sum(case when s.dynamic_cost_czk > 0 then s.dynamic_cost_czk else 0 end), 2) as import_cost_czk,
      round(sum(case when s.dynamic_cost_czk < 0 then abs(s.dynamic_cost_czk) else 0 end), 2) as export_revenue_czk,
      round(sum(s.dynamic_cost_czk), 2) as net_cost_czk,
      round(coalesce(sum(s.green_bonus_czk), 0), 2) as green_bonus_czk,
      round(-sum(s.dynamic_cost_czk) + coalesce(sum(s.green_bonus_czk), 0), 2) as total_balance_czk,
      round(sum(s.planned_cost_czk), 2) as planned_net_cost_czk,
      round(-coalesce(sum(s.planned_cost_czk), 0), 2) as planned_balance_czk,
      round(sum(s.dynamic_cost_czk) - coalesce(sum(s.planned_cost_czk), 0), 2) as deviation_cost_czk
    from slots s
    group by s.site_id, s.day_local
  )
  select
    d.site_id,
    d.day_local,
    d.interval_count,
    d.import_kwh,
    d.export_kwh,
    d.pv_kwh,
    d.load_kwh,
    d.pv_self_consumption_kwh,
    d.ev_kwh,
    d.hp_kwh,
    d.grid_import_cashflow_czk,
    d.grid_export_revenue_czk,
    d.import_cost_czk,
    d.export_revenue_czk,
    d.net_cost_czk,
    d.green_bonus_czk,
    d.total_balance_czk,
    d.planned_net_cost_czk,
    d.planned_balance_czk,
    d.deviation_cost_czk
  from daily_agg d
  order by d.day_local;
$fn$;

comment on function ems.fn_economics_daily_for_window is
  'Denní souhrn ekonomiky [from, to); ceny inline dle fn_effective_buy/sell (R__011), bez PL volání per slot.';

create or replace function ems.fn_economics_daily_month(
  p_site_id int,
  p_month_start date,
  p_month_end date
)
returns jsonb
language sql
stable
as $fn$
  select jsonb_build_object(
    'has_green_bonus',
    exists (
      select 1
      from ems.asset_pv_array apv
      where apv.site_id = p_site_id
        and apv.green_bonus_czk_kwh is not null
    ),
    'days',
    coalesce(
      (
        select jsonb_agg(sub.day_row order by sub.day_local)
        from (
          select
            r.day_local,
            jsonb_build_object(
              'day', r.day_local,
              'interval_count', r.interval_count,
              'import_kwh', r.import_kwh,
              'export_kwh', r.export_kwh,
              'pv_kwh', r.pv_kwh,
              'load_kwh', r.load_kwh,
              'pv_self_consumption_kwh', r.pv_self_consumption_kwh,
              'ev_kwh', r.ev_kwh,
              'hp_kwh', r.hp_kwh,
              'import_cost_czk',
              case when l.site_id is not null then l.import_cost_czk else r.import_cost_czk end,
              'export_revenue_czk',
              case when l.site_id is not null then l.export_revenue_czk else r.export_revenue_czk end,
              'grid_import_cashflow_czk',
              coalesce(l.grid_import_cashflow_czk, r.grid_import_cashflow_czk),
              'grid_export_revenue_czk',
              coalesce(l.grid_export_revenue_czk, r.grid_export_revenue_czk),
              'net_cost_czk',
              case when l.site_id is not null then l.net_cost_czk else r.net_cost_czk end,
              'green_bonus_czk',
              case when l.site_id is not null then l.green_bonus_czk else r.green_bonus_czk end,
              'total_balance_czk',
              case when l.site_id is not null then l.total_balance_czk else r.total_balance_czk end,
              'planned_balance_czk', r.planned_balance_czk,
              'deviation_cost_czk', r.deviation_cost_czk,
              'is_locked', (l.site_id is not null)
            ) as day_row
          from ems.fn_economics_daily_for_window(
            p_site_id,
            (p_month_start::timestamp at time zone 'Europe/Prague'),
            (p_month_end::timestamp at time zone 'Europe/Prague')
          ) r
          left join ems.audit_day_lock l
            on l.site_id = r.site_id
           and l.day_local = r.day_local
          order by r.day_local
        ) sub
      ),
      '[]'::jsonb
    )
  );
$fn$;

comment on function ems.fn_economics_daily_month(int, date, date) is
  'Měsíční denní ekonomika + lock merge jako JSON (GET /economics/daily).';
