-- =============================================================
-- R__031_fn_ote_import.sql
-- OTE CZ import – parser a import funkce
-- Repeatable migration – při změně funkce stačí upravit tento soubor
-- =============================================================

-- Parser: raw jsonb → 96 (nebo 92/100 při DST) cenových řádků po 15 min
CREATE OR REPLACE FUNCTION ems.fn_ote_parse_15m_price_json(
  in_payload     jsonb,
  in_czk_per_eur numeric DEFAULT 25.000
)
RETURNS TABLE (
  interval_start    timestamptz,
  interval_end      timestamptz,
  raw_price_czk_kwh numeric(10,6)
)
LANGUAGE plpgsql
AS $$
DECLARE
  v_date_text   text;
  v_market_date date;
  v_anchor      timestamptz;
  v_dl          jsonb;
  v_npts        int;
BEGIN
  IF in_payload IS NULL THEN
    RAISE EXCEPTION 'in_payload must not be null';
  END IF;
  IF in_czk_per_eur IS NULL OR in_czk_per_eur <= 0 THEN
    RAISE EXCEPTION 'in_czk_per_eur must be > 0, got: %', in_czk_per_eur;
  END IF;

  -- Datum z graph.title ve formátu "... DD.MM.YYYY"
  v_date_text := substring(
    in_payload #>> '{graph,title}'
    FROM '([0-9]{2}\.[0-9]{2}\.[0-9]{4})'
  );
  IF v_date_text IS NULL THEN
    RAISE EXCEPTION 'cannot parse date from graph.title: %',
      in_payload #>> '{graph,title}';
  END IF;
  v_market_date := to_date(v_date_text, 'DD.MM.YYYY');
  -- Začátek obchodního dne v Praze jako absolutní okamžik; pak +15 min v UTC.
  -- Starý vzor (timestamp + interval) AT TIME ZONE při přechodu na letní čas
  -- slučoval různé sloty → duplicitní interval_start a chyba ON CONFLICT.
  v_anchor := v_market_date::timestamp AT TIME ZONE 'Europe/Prague';

  -- OTE mění strukturu: novější data = 15min série (tooltip flash_chart_01_y_15m_*),
  -- starší / jiná odpověď = jen 24 hodinových bodů s tooltip "Cena".
  SELECT t.dl INTO v_dl
  FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl)
  WHERE dl ->> 'tooltip' = 'flash_chart_01_y_15m_price_tooltip'
    AND jsonb_array_length(dl -> 'point') >= 90
  LIMIT 1;

  IF v_dl IS NULL THEN
    SELECT t.dl INTO v_dl
    FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl)
    WHERE dl ->> 'tooltip' = 'flash_chart_01_y_60m_price_tooltip'
      AND jsonb_array_length(dl -> 'point') >= 90
    LIMIT 1;
  END IF;

  IF v_dl IS NULL THEN
    SELECT t.dl INTO v_dl
    FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl)
    WHERE dl ->> 'tooltip' = 'Cena'
      AND jsonb_array_length(dl -> 'point') >= 90
    LIMIT 1;
  END IF;

  IF v_dl IS NULL THEN
    SELECT t.dl INTO v_dl
    FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl)
    WHERE dl ->> 'tooltip' = 'Cena'
      AND jsonb_array_length(dl -> 'point') BETWEEN 2 AND 28
    LIMIT 1;
  END IF;

  IF v_dl IS NULL THEN
    SELECT t.dl INTO v_dl
    FROM jsonb_array_elements(in_payload #> '{data,dataLine}') AS t(dl)
    WHERE dl ->> 'tooltip' = 'flash_chart_01_y_15m_price_tooltip'
      AND jsonb_array_length(dl -> 'point') BETWEEN 2 AND 28
    LIMIT 1;
  END IF;

  IF v_dl IS NULL THEN
    RAISE EXCEPTION
      'OTE price dataLine not found (očekáváno 15min flash_* nebo hodinová Cena); '
      'dostupné tooltips: %',
      (SELECT jsonb_agg(dl ->> 'tooltip' ORDER BY dl ->> 'tooltip')
       FROM jsonb_array_elements(in_payload #> '{data,dataLine}') dl);
  END IF;

  v_npts := jsonb_array_length(v_dl -> 'point');

  IF v_npts >= 90 THEN
    -- x = 1..N = 15min bloky (96 běžně; 92 při zkráceném dni při přechodu na letní čas)
    RETURN QUERY
    SELECT s.interval_start, s.interval_end, s.raw_price_czk_kwh
    FROM (
      SELECT
        (v_anchor + ((block_no - 1) * INTERVAL '15 minutes'))   AS interval_start,
        (v_anchor + (block_no * INTERVAL '15 minutes'))         AS interval_end,
        ROUND(
          (price_eur_mwh * in_czk_per_eur / 1000.0)::numeric, 6
        )::numeric(10,6)                                       AS raw_price_czk_kwh,
        block_no
      FROM (
        SELECT
          (p ->> 'x')::int     AS block_no,
          (p ->> 'y')::numeric AS price_eur_mwh
        FROM jsonb_array_elements(v_dl -> 'point') AS p
      ) pts
    ) AS s
    ORDER BY s.block_no;
  ELSIF v_npts BETWEEN 2 AND 28 THEN
    -- x = 1..H = hodiny (ne nutně celý den); každou hodinovou cenu → 4× 15min slot
    RETURN QUERY
    SELECT s.interval_start, s.interval_end, s.raw_price_czk_kwh
    FROM (
      SELECT
        (v_anchor + (((hour_no - 1) * 4 + qix) * INTERVAL '15 minutes'))
          AS interval_start,
        (v_anchor + (((hour_no - 1) * 4 + qix) * INTERVAL '15 minutes')
            + INTERVAL '15 minutes')
          AS interval_end,
        ROUND(
          (price_eur_mwh * in_czk_per_eur / 1000.0)::numeric, 6
        )::numeric(10,6)                                       AS raw_price_czk_kwh,
        (hour_no - 1) * 4 + qix + 1                            AS sort_key
      FROM (
        SELECT (p ->> 'x')::int AS hour_no, (p ->> 'y')::numeric AS price_eur_mwh
        FROM jsonb_array_elements(v_dl -> 'point') AS p
      ) h
      CROSS JOIN (VALUES (0), (1), (2), (3)) AS q(qix)
    ) AS s
    ORDER BY s.sort_key;
  ELSE
    RAISE EXCEPTION
      'OTE price series: unexpected point count % (tooltip %)',
      v_npts,
      v_dl ->> 'tooltip';
  END IF;

  IF NOT FOUND THEN
    RAISE EXCEPTION 'OTE price series had no points after parse';
  END IF;
END;
$$;

COMMENT ON FUNCTION ems.fn_ote_parse_15m_price_json(jsonb, numeric) IS
'Parsuje raw JSON z OTE @@chart-data?time_resolution=PT15M.
Datum z graph.title (DD.MM.YYYY). Sloty: kotva půlnoci Europe/Prague jako timestamptz + násobky 15 min (bez DST duplicit).
Výběr série: flash_15m ≥90, flash_60m ≥90, Cena ≥90, Cena 2–28 h, flash_15m 2–28 h.
EUR/MWh → Kč/kWh. Typicky 96 řádků, 92 při zkráceném dni (letní čas).
Testování přímo v DB:
  SELECT * FROM ems.fn_ote_parse_15m_price_json(pg_read_file(''/tmp/ote.json'')::jsonb, 25.0) LIMIT 5;';


CREATE OR REPLACE FUNCTION ems.fn_ote_import_from_json(
  in_payload     jsonb,
  in_czk_per_eur numeric DEFAULT 25.000
)
RETURNS integer
LANGUAGE plpgsql
AS $$
DECLARE
  v_rowcount integer;
BEGIN
  INSERT INTO ems.market_interval_price (
    market_source,
    interval_start,
    interval_end,
    buy_raw_price_czk_kwh,
    sell_raw_price_czk_kwh,
    currency,
    imported_at
  )
  SELECT DISTINCT ON (p.interval_start)
    'OTE_CZ',
    p.interval_start,
    p.interval_end,
    p.raw_price_czk_kwh,
    p.raw_price_czk_kwh,   -- spot trh: buy = sell
    'CZK',
    now()
  FROM ems.fn_ote_parse_15m_price_json(in_payload, in_czk_per_eur) AS p
  ORDER BY p.interval_start, p.interval_end DESC
  ON CONFLICT (market_source, interval_start) DO UPDATE SET
    interval_end           = EXCLUDED.interval_end,
    buy_raw_price_czk_kwh  = EXCLUDED.buy_raw_price_czk_kwh,
    sell_raw_price_czk_kwh = EXCLUDED.sell_raw_price_czk_kwh,
    imported_at            = EXCLUDED.imported_at;

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

COMMENT ON FUNCTION ems.fn_ote_import_from_json(jsonb, numeric) IS
'Uloží výstup fn_ote_parse_15m_price_json do ems.market_interval_price.
Python předá raw jsonb z HTTP response + kurz EUR/CZK.
Vrátí počet upsertnutých řádků (očekáváno 96).
Testování přímo v DB:
  SELECT ems.fn_ote_import_from_json(
    pg_read_file(''/tmp/ote.json'')::jsonb, 25.0
  );';
