CREATE OR REPLACE FUNCTION mv_reports_ucepsa_demo.oee_kpis(
    p_from timestamptz,
    p_to timestamptz,
    p_machine_id text DEFAULT 'CORT-01',
    p_asset text DEFAULT 'revpi_oee_node_01'
)
RETURNS TABLE (
    oee_pct numeric,
    availability_pct numeric,
    performance_pct numeric,
    quality_pct numeric
)
LANGUAGE sql
STABLE
AS $$
WITH dashboard_bounds AS (
    SELECT
        p_from AS dashboard_from,
        LEAST(p_to, now()) AS dashboard_to
),
telemetry_span AS (
    SELECT
        MIN(ingest_ts) AS first_ts,
        MAX(ingest_ts) AS last_ts
    FROM mv_hot.edge_oee_live_telemetry
    WHERE asset = p_asset
      AND ingest_ts >= (SELECT dashboard_from FROM dashboard_bounds)
      AND ingest_ts <= (SELECT dashboard_to FROM dashboard_bounds)
),
bounds AS (
    SELECT
        CASE
            WHEN first_ts IS NULL THEN dashboard_from
            ELSE GREATEST(dashboard_from, first_ts)
        END AS t_from,
        CASE
            WHEN last_ts IS NULL THEN dashboard_to
            ELSE LEAST(dashboard_to, last_ts)
        END AS t_to
    FROM dashboard_bounds
    CROSS JOIN telemetry_span
),
target AS (
    SELECT
        COALESCE((
            SELECT mt.target_cycle_rate_ppm
            FROM mv_hot.edge_oee_machine_targets mt
            WHERE mt.machine_id = p_machine_id
            LIMIT 1
        ), 10.00)::numeric AS target_cycle_rate_ppm,

        COALESCE((
            SELECT mt.quality_pct_default
            FROM mv_hot.edge_oee_machine_targets mt
            WHERE mt.machine_id = p_machine_id
            LIMIT 1
        ), 100.00)::numeric AS quality_pct_default
),
planned AS (
    SELECT
        GREATEST(EXTRACT(EPOCH FROM (t_to - t_from))::numeric, 0) AS planned_s
    FROM bounds
),
stops AS (
    SELECT
        COALESCE(
            SUM(
                GREATEST(
                    EXTRACT(EPOCH FROM (
                        LEAST(COALESCE(s.ended_at, b.t_to), b.t_to)
                        -
                        GREATEST(
                            CASE
                                WHEN s.started_at > s.created_at + interval '5 seconds'
                                    THEN s.created_at
                                ELSE s.started_at
                            END,
                            b.t_from
                        )
                    ))::numeric,
                    0
                )
            ) FILTER (
                WHERE s.started_at < b.t_to
                  AND COALESCE(s.ended_at, now()) > b.t_from
            ),
            0
        )::numeric AS stop_s
    FROM mv_hot.edge_oee_machine_stops s
    CROSS JOIN bounds b
    WHERE s.machine_id = p_machine_id
),
cycles AS (
    SELECT
        COALESCE(
            GREATEST(
                MAX(cycle_pulse_count) - MIN(cycle_pulse_count),
                0
            ),
            0
        )::numeric AS actual_cycles
    FROM mv_hot.edge_oee_live_telemetry l
    CROSS JOIN bounds b
    WHERE l.asset = p_asset
      AND l.ingest_ts >= b.t_from
      AND l.ingest_ts <= b.t_to
      AND l.cycle_pulse_count IS NOT NULL
),
calc AS (
    SELECT
        p.planned_s,
        st.stop_s,
        GREATEST(p.planned_s - st.stop_s, 0)::numeric AS running_s,
        c.actual_cycles,
        t.target_cycle_rate_ppm,
        t.quality_pct_default,

        CASE
            WHEN p.planned_s > 0
            THEN ROUND(((GREATEST(p.planned_s - st.stop_s, 0) / p.planned_s) * 100)::numeric, 2)
            ELSE 0
        END AS availability_pct,

        CASE
            WHEN GREATEST(p.planned_s - st.stop_s, 0) > 0
             AND t.target_cycle_rate_ppm > 0
            THEN ROUND(
                LEAST(
                    100::numeric,
                    (
                        c.actual_cycles
                        /
                        NULLIF(
                            t.target_cycle_rate_ppm * (GREATEST(p.planned_s - st.stop_s, 0) / 60.0),
                            0
                        )
                    ) * 100
                )::numeric,
                2
            )
            ELSE 0
        END AS performance_pct,

        t.quality_pct_default AS quality_pct
    FROM planned p
    CROSS JOIN stops st
    CROSS JOIN cycles c
    CROSS JOIN target t
)
SELECT
    ROUND(((calc.availability_pct * calc.performance_pct * calc.quality_pct) / 10000.0)::numeric, 2) AS oee_pct,
    calc.availability_pct,
    calc.performance_pct,
    calc.quality_pct
FROM calc;
$$;


CREATE OR REPLACE FUNCTION mv_reports_ucepsa_demo.oee_operational_data(
    p_from timestamptz,
    p_to timestamptz,
    p_machine_id text DEFAULT 'CORT-01',
    p_asset text DEFAULT 'revpi_oee_node_01'
)
RETURNS TABLE (
    running_min numeric,
    stop_min numeric,
    actual_cycles numeric
)
LANGUAGE sql
STABLE
AS $$
WITH dashboard_bounds AS (
    SELECT
        p_from AS dashboard_from,
        LEAST(p_to, now()) AS dashboard_to
),
telemetry_span AS (
    SELECT
        MIN(ingest_ts) AS first_ts,
        MAX(ingest_ts) AS last_ts
    FROM mv_hot.edge_oee_live_telemetry
    WHERE asset = p_asset
      AND ingest_ts >= (SELECT dashboard_from FROM dashboard_bounds)
      AND ingest_ts <= (SELECT dashboard_to FROM dashboard_bounds)
),
bounds AS (
    SELECT
        CASE
            WHEN first_ts IS NULL THEN dashboard_from
            ELSE GREATEST(dashboard_from, first_ts)
        END AS t_from,
        CASE
            WHEN last_ts IS NULL THEN dashboard_to
            ELSE LEAST(dashboard_to, last_ts)
        END AS t_to
    FROM dashboard_bounds
    CROSS JOIN telemetry_span
),
planned AS (
    SELECT
        GREATEST(EXTRACT(EPOCH FROM (t_to - t_from))::numeric, 0) AS planned_s
    FROM bounds
),
stops AS (
    SELECT
        COALESCE(
            SUM(
                GREATEST(
                    EXTRACT(EPOCH FROM (
                        LEAST(COALESCE(s.ended_at, b.t_to), b.t_to)
                        -
                        GREATEST(
                            CASE
                                WHEN s.started_at > s.created_at + interval '5 seconds'
                                    THEN s.created_at
                                ELSE s.started_at
                            END,
                            b.t_from
                        )
                    ))::numeric,
                    0
                )
            ) FILTER (
                WHERE s.started_at < b.t_to
                  AND COALESCE(s.ended_at, now()) > b.t_from
            ),
            0
        )::numeric AS stop_s
    FROM mv_hot.edge_oee_machine_stops s
    CROSS JOIN bounds b
    WHERE s.machine_id = p_machine_id
),
cycles AS (
    SELECT
        COALESCE(
            GREATEST(
                MAX(cycle_pulse_count) - MIN(cycle_pulse_count),
                0
            ),
            0
        )::numeric AS actual_cycles
    FROM mv_hot.edge_oee_live_telemetry l
    CROSS JOIN bounds b
    WHERE l.asset = p_asset
      AND l.ingest_ts >= b.t_from
      AND l.ingest_ts <= b.t_to
      AND l.cycle_pulse_count IS NOT NULL
),
calc AS (
    SELECT
        p.planned_s,
        st.stop_s,
        GREATEST(p.planned_s - st.stop_s, 0)::numeric AS running_s,
        c.actual_cycles
    FROM planned p
    CROSS JOIN stops st
    CROSS JOIN cycles c
)
SELECT
    ROUND((running_s / 60.0)::numeric, 2) AS running_min,
    ROUND((stop_s / 60.0)::numeric, 2) AS stop_min,
    actual_cycles
FROM calc;
$$;


GRANT EXECUTE ON FUNCTION mv_reports_ucepsa_demo.oee_kpis(timestamptz, timestamptz, text, text)
TO grafana_ucepsa_ro;

GRANT EXECUTE ON FUNCTION mv_reports_ucepsa_demo.oee_operational_data(timestamptz, timestamptz, text, text)
TO grafana_ucepsa_ro;
