BEGIN;

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_stop_candidates_v1 AS
WITH stop_base AS (
    SELECT
        s.*,
        COALESCE(
            s.duration_s,
            EXTRACT(EPOCH FROM (COALESCE(s.ended_at, now()) - s.started_at))::numeric
        ) AS effective_duration_s,
        COALESCE(
            s.cost_eur,
            ROUND((
                EXTRACT(EPOCH FROM (COALESCE(s.ended_at, now()) - s.started_at))
                / 3600.0 * s.value_hour_eur
            )::numeric, 2)
        ) AS effective_capacity_cost_eur
    FROM mv_hot.edge_oee_machine_stops s
    WHERE s.tenant = 'ucepsa'
      AND s.site = 'ucepsa_onpremise'
), enriched AS (
    SELECT
        s.*,
        d.id AS deployment_id,
        d.scope_code,
        d.mode AS deployment_mode,
        d.planned_go_live_date,
        d.actual_go_live_at,
        p.noise_below_s,
        p.aggregate_below_s,
        p.operator_individual_from_s,
        p.aggregation_window_min,
        p.require_valid_order,
        p.require_closed_event,
        o.id AS order_state_event_id,
        o.effective_from AS order_context_from,
        o.effective_to AS order_context_to,
        o.odoo_state,
        o.order_present,
        o.order_coherent,
        o.workorder_id AS odoo_workorder_id,
        o.workorder_name,
        o.workorder_state,
        o.production_id AS odoo_production_id,
        o.production_order AS order_ref,
        o.product_id,
        o.product AS product_name,
        o.qty_production,
        o.date_start AS odoo_date_start,
        o.date_finished AS odoo_date_finished,
        pc.units_per_cycle,
        pc.target_cycle_rate_ppm,
        ep.parameter_value AS configured_capacity_eur_h,
        ep.confidence AS capacity_cost_confidence
    FROM stop_base s
    LEFT JOIN LATERAL (
        SELECT x.*
        FROM mv_loss_intelligence.deployment_config x
        WHERE x.tenant = s.tenant
          AND x.site = s.site
          AND x.scope_code = 'CUTTERS'
        ORDER BY x.id DESC
        LIMIT 1
    ) d ON true
    LEFT JOIN LATERAL (
        SELECT x.*
        FROM mv_loss_intelligence.loss_policies x
        WHERE x.tenant = s.tenant
          AND x.site = s.site
          AND x.policy_code = 'CUTTER_STOPS_V1'
          AND x.is_active = true
        ORDER BY x.id DESC
        LIMIT 1
    ) p ON true
    LEFT JOIN LATERAL (
        SELECT x.*
        FROM mv_reports_ucepsa_prod.li_order_state_intervals_v1 x
        WHERE x.tenant = s.tenant
          AND x.site = s.site
          AND x.machine_id = s.machine_id
          AND s.started_at >= x.effective_from
          AND (x.effective_to IS NULL OR s.started_at < x.effective_to)
        ORDER BY x.effective_from DESC, x.id DESC
        LIMIT 1
    ) o ON true
    LEFT JOIN LATERAL (
        SELECT x.*
        FROM mv_loss_intelligence.product_cycle_parameters x
        WHERE x.tenant = s.tenant
          AND x.site = s.site
          AND x.is_active = true
          AND x.product_id = o.product_id
          AND (x.machine_id IS NULL OR x.machine_id = s.machine_id)
          AND s.started_at >= x.valid_from
          AND (x.valid_to IS NULL OR s.started_at < x.valid_to)
        ORDER BY (x.machine_id IS NOT NULL) DESC, x.valid_from DESC, x.id DESC
        LIMIT 1
    ) pc ON true
    LEFT JOIN LATERAL (
        SELECT x.*
        FROM mv_loss_intelligence.economic_parameters x
        WHERE x.tenant = s.tenant
          AND x.site = s.site
          AND x.parameter_code = 'CAPACITY_HOUR_EUR'
          AND x.is_active = true
          AND (x.machine_id IS NULL OR x.machine_id = s.machine_id)
          AND s.started_at >= x.valid_from
          AND (x.valid_to IS NULL OR s.started_at < x.valid_to)
        ORDER BY (x.machine_id IS NOT NULL) DESC, x.valid_from DESC, x.id DESC
        LIMIT 1
    ) ep ON true
)
SELECT
    e.id AS source_stop_id,
    e.tenant,
    e.site,
    e.deployment_id,
    e.scope_code,
    e.deployment_mode,
    e.planned_go_live_date,
    e.actual_go_live_at,
    CASE
        WHEN e.deployment_mode = 'ACTIVE'
         AND e.actual_go_live_at IS NOT NULL
         AND e.started_at >= e.actual_go_live_at
            THEN 'ACTIVE'
        ELSE 'SHADOW'
    END AS ledger_phase,
    e.machine_id,
    e.asset,
    e.started_at,
    e.ended_at,
    e.status AS source_status,
    e.classification_status,
    e.cause_code,
    c.cause_name,
    c.is_planned,
    e.operator_note,
    e.start_event_name,
    e.end_event_name,
    e.effective_duration_s,
    ROUND((e.effective_duration_s / 60.0)::numeric, 2) AS effective_duration_min,
    e.value_hour_eur AS source_value_hour_eur,
    e.configured_capacity_eur_h,
    COALESCE(e.configured_capacity_eur_h, e.value_hour_eur) AS applied_capacity_eur_h,
    ROUND((
        e.effective_duration_s / 3600.0
        * COALESCE(e.configured_capacity_eur_h, e.value_hour_eur)
    )::numeric, 2) AS effective_capacity_cost_eur,
    e.capacity_cost_confidence,
    e.order_state_event_id,
    e.order_context_from,
    e.order_context_to,
    e.odoo_state,
    e.order_present,
    e.order_coherent,
    e.odoo_workorder_id,
    e.workorder_name,
    e.workorder_state,
    e.odoo_production_id,
    e.order_ref,
    e.product_id,
    e.product_name,
    e.qty_production,
    e.odoo_date_start,
    e.odoo_date_finished,
    e.units_per_cycle,
    e.target_cycle_rate_ppm,
    CASE
        WHEN e.deployment_id IS NULL THEN 'CONFIG_MISSING'
        WHEN e.noise_below_s IS NULL THEN 'POLICY_MISSING'
        WHEN e.status = 'OPEN' THEN 'OPEN_MONITORING'
        WHEN e.require_closed_event AND e.status <> 'CLOSED' THEN 'EXCLUDED_NOT_CLOSED'
        WHEN e.order_state_event_id IS NULL THEN 'EXCLUDED_NO_ORDER_CONTEXT'
        WHEN e.require_valid_order AND COALESCE(e.order_present, false) = false THEN 'EXCLUDED_NO_VALID_ORDER'
        WHEN e.require_valid_order AND COALESCE(e.order_coherent, false) = false THEN 'EXCLUDED_ORDER_INCOHERENT'
        WHEN e.effective_duration_s < e.noise_below_s THEN 'EXCLUDED_NOISE'
        WHEN e.effective_duration_s < e.operator_individual_from_s THEN 'CANDIDATE_GROUP'
        ELSE 'CANDIDATE_INDIVIDUAL'
    END AS eligibility_status,
    CASE
        WHEN e.status = 'CLOSED'
         AND e.order_state_event_id IS NOT NULL
         AND (NOT e.require_valid_order OR (e.order_present = true AND e.order_coherent = true))
         AND e.effective_duration_s >= e.noise_below_s
            THEN true
        ELSE false
    END AS technically_eligible,
    CASE
        WHEN e.deployment_mode = 'ACTIVE'
         AND e.actual_go_live_at IS NOT NULL
         AND e.started_at >= e.actual_go_live_at
         AND e.status = 'CLOSED'
         AND e.order_state_event_id IS NOT NULL
         AND (NOT e.require_valid_order OR (e.order_present = true AND e.order_coherent = true))
         AND e.effective_duration_s >= e.noise_below_s
            THEN true
        ELSE false
    END AS official_ledger_eligible,
    jsonb_build_object(
        'source_stop_id', e.id,
        'source_start_payload', e.start_payload_json,
        'source_end_payload', e.end_payload_json,
        'order_state_event_id', e.order_state_event_id,
        'deployment_mode', e.deployment_mode,
        'policy', jsonb_build_object(
            'noise_below_s', e.noise_below_s,
            'aggregate_below_s', e.aggregate_below_s,
            'operator_individual_from_s', e.operator_individual_from_s,
            'aggregation_window_min', e.aggregation_window_min
        )
    ) AS evidence_json
FROM enriched e
LEFT JOIN mv_hot.edge_oee_stop_causes c
  ON c.cause_code = e.cause_code;

COMMENT ON VIEW mv_reports_ucepsa_prod.li_stop_candidates_v1 IS
'Clasifica cada paro raw como excluido, agrupable, individual u abierto. Mantiene separados el modo SHADOW y la elegibilidad oficial.';

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_stop_groups_v1 AS
WITH candidates AS (
    SELECT
        c.*,
        COALESCE((c.evidence_json #>> '{policy,aggregation_window_min}')::int, 15) AS aggregation_window_min
    FROM mv_reports_ucepsa_prod.li_stop_candidates_v1 c
    WHERE c.eligibility_status IN ('CANDIDATE_GROUP', 'CANDIDATE_INDIVIDUAL')
), bucketed AS (
    SELECT
        c.*,
        date_bin(
            make_interval(mins => c.aggregation_window_min),
            c.started_at,
            TIMESTAMPTZ '2000-01-01 00:00:00+00'
        ) AS bucket_start
    FROM candidates c
), grouped_micro AS (
    SELECT
        tenant,
        site,
        deployment_id,
        ledger_phase,
        machine_id,
        asset,
        order_ref,
        odoo_production_id,
        NULLIF(odoo_workorder_id, '')::bigint AS odoo_workorder_id,
        product_id,
        product_name,
        units_per_cycle,
        target_cycle_rate_ppm,
        COALESCE(cause_code, 'PENDING') AS cause_code,
        COALESCE(cause_name, 'Pendiente de clasificar') AS cause_name,
        classification_status,
        bucket_start AS group_window_start,
        COUNT(*)::int AS event_count,
        MIN(started_at) AS started_at,
        MAX(ended_at) AS ended_at,
        SUM(effective_duration_s)::numeric AS duration_s,
        ROUND(SUM(effective_capacity_cost_eur)::numeric, 2) AS capacity_loss_eur,
        array_agg(source_stop_id ORDER BY started_at) AS source_stop_ids,
        bool_and(official_ledger_eligible) AS official_ledger_eligible
    FROM bucketed
    WHERE eligibility_status = 'CANDIDATE_GROUP'
    GROUP BY
        tenant, site, deployment_id, ledger_phase, machine_id, asset,
        order_ref, odoo_production_id, odoo_workorder_id, product_id,
        product_name, units_per_cycle, target_cycle_rate_ppm,
        COALESCE(cause_code, 'PENDING'),
        COALESCE(cause_name, 'Pendiente de clasificar'),
        classification_status, bucket_start
), individual AS (
    SELECT
        tenant,
        site,
        deployment_id,
        ledger_phase,
        machine_id,
        asset,
        order_ref,
        odoo_production_id,
        NULLIF(odoo_workorder_id, '')::bigint AS odoo_workorder_id,
        product_id,
        product_name,
        units_per_cycle,
        target_cycle_rate_ppm,
        COALESCE(cause_code, 'PENDING') AS cause_code,
        COALESCE(cause_name, 'Pendiente de clasificar') AS cause_name,
        classification_status,
        started_at AS group_window_start,
        1::int AS event_count,
        started_at,
        ended_at,
        effective_duration_s::numeric AS duration_s,
        effective_capacity_cost_eur::numeric AS capacity_loss_eur,
        ARRAY[source_stop_id]::bigint[] AS source_stop_ids,
        official_ledger_eligible
    FROM bucketed
    WHERE eligibility_status = 'CANDIDATE_INDIVIDUAL'
), combined AS (
    SELECT * FROM grouped_micro
    UNION ALL
    SELECT * FROM individual
)
SELECT
    CASE
        WHEN event_count = 1 THEN 'STOP:' || source_stop_ids[1]::text
        ELSE 'STOPGROUP:' || machine_id || ':'
             || COALESCE(odoo_production_id::text, COALESCE(order_ref, 'NO_ORDER')) || ':'
             || to_char(group_window_start AT TIME ZONE 'Europe/Madrid', 'YYYYMMDDHH24MI') || ':'
             || cause_code
    END AS source_key,
    *,
    ROUND((duration_s / 60.0)::numeric, 2) AS duration_min,
    CASE
        WHEN duration_s >= 900 THEN 'HIGH'
        WHEN duration_s >= 300 THEN 'MEDIUM'
        ELSE 'LOW'
    END AS preliminary_priority
FROM combined;

COMMENT ON VIEW mv_reports_ucepsa_prod.li_stop_groups_v1 IS
'Agrupa microparos por máquina, orden, ventana y causa. Los paros largos permanecen individuales.';

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_shadow_review_queue_v1 AS
SELECT
    row_number() OVER (
        ORDER BY capacity_loss_eur DESC, duration_s DESC, started_at DESC
    ) AS shadow_rank,
    source_key,
    machine_id,
    order_ref,
    product_name,
    cause_code,
    cause_name,
    event_count,
    started_at,
    ended_at,
    duration_min,
    capacity_loss_eur,
    preliminary_priority,
    'SHADOW_ONLY_NOT_OFFICIAL'::text AS queue_status,
    official_ledger_eligible,
    source_stop_ids
FROM mv_reports_ucepsa_prod.li_stop_groups_v1
WHERE ledger_phase = 'SHADOW'
ORDER BY shadow_rank;

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_operator_stop_queue_v1 AS
SELECT
    source_key,
    machine_id,
    order_ref,
    product_name,
    cause_code,
    cause_name,
    classification_status,
    event_count,
    started_at,
    ended_at,
    duration_min,
    capacity_loss_eur,
    source_stop_ids
FROM mv_reports_ucepsa_prod.li_stop_groups_v1
WHERE ledger_phase = 'ACTIVE'
  AND official_ledger_eligible = true
  AND classification_status = 'PENDING'
ORDER BY capacity_loss_eur DESC, duration_s DESC, started_at DESC;

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_daily_review_queue_v1 AS
SELECT
    row_number() OVER (
        ORDER BY
            COALESCE(l.priority_score, 0) DESC,
            l.review_due_at NULLS LAST,
            l.detected_at
    ) AS review_rank,
    l.loss_id,
    l.machine_id,
    l.order_ref,
    l.product_name,
    l.loss_type,
    l.cause_code,
    ROUND((l.duration_s / 60.0)::numeric, 2) AS duration_min,
    l.capacity_loss_eur,
    l.energy_waste_eur,
    l.priority_level,
    l.action_candidate,
    l.action_final,
    l.review_owner,
    l.review_due_at,
    l.status,
    t.trial_id,
    t.trial_code,
    t.result AS trial_result,
    t.decision AS trial_decision
FROM mv_loss_intelligence.loss_ledger l
LEFT JOIN LATERAL (
    SELECT x.*
    FROM mv_loss_intelligence.action_trials x
    WHERE x.loss_id = l.loss_id
    ORDER BY x.created_at DESC, x.trial_id DESC
    LIMIT 1
) t ON true
WHERE l.tenant = 'ucepsa'
  AND l.site = 'ucepsa_onpremise'
  AND l.ledger_phase = 'ACTIVE'
  AND l.status NOT IN ('CLOSED', 'DISCARDED')
ORDER BY review_rank
LIMIT 5;

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_shadow_summary_v1 AS
SELECT
    ledger_phase,
    eligibility_status,
    machine_id,
    COUNT(*) AS stop_count,
    ROUND(SUM(effective_duration_s / 60.0)::numeric, 2) AS total_minutes,
    ROUND(SUM(effective_capacity_cost_eur)::numeric, 2) AS capacity_cost_eur
FROM mv_reports_ucepsa_prod.li_stop_candidates_v1
GROUP BY ledger_phase, eligibility_status, machine_id;

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_product_cycle_coverage_v1 AS
SELECT
    c.machine_id,
    c.odoo_production_id,
    c.order_ref,
    c.product_id,
    c.product_name,
    c.qty_production,
    c.units_per_cycle,
    c.target_cycle_rate_ppm,
    CASE
        WHEN c.product_id IS NULL THEN 'MISSING_PRODUCT_ID'
        WHEN c.units_per_cycle IS NULL THEN 'MISSING_UNITS_PER_CYCLE'
        WHEN c.target_cycle_rate_ppm IS NULL THEN 'MISSING_TARGET_CYCLE_RATE'
        ELSE 'READY'
    END AS cycle_parameter_status,
    MAX(c.started_at) AS latest_stop_started_at,
    COUNT(*) AS related_stop_count
FROM mv_reports_ucepsa_prod.li_stop_candidates_v1 c
WHERE c.order_present = true
  AND c.order_coherent = true
GROUP BY
    c.machine_id, c.odoo_production_id, c.order_ref, c.product_id,
    c.product_name, c.qty_production, c.units_per_cycle, c.target_cycle_rate_ppm;

CREATE OR REPLACE VIEW mv_reports_ucepsa_prod.li_data_quality_v1 AS
WITH deployment AS (
    SELECT COUNT(*) AS n
    FROM mv_loss_intelligence.deployment_config
    WHERE tenant = 'ucepsa' AND site = 'ucepsa_onpremise' AND scope_code = 'CUTTERS'
), capacity_cost AS (
    SELECT COUNT(*) AS n
    FROM mv_loss_intelligence.economic_parameters
    WHERE tenant = 'ucepsa' AND site = 'ucepsa_onpremise'
      AND parameter_code = 'CAPACITY_HOUR_EUR' AND is_active = true
), energy_cost AS (
    SELECT COUNT(*) AS n
    FROM mv_loss_intelligence.economic_parameters
    WHERE tenant = 'ucepsa' AND site = 'ucepsa_onpremise'
      AND parameter_code = 'ENERGY_VARIABLE_EUR_KWH' AND is_active = true
), current_loaded AS (
    SELECT COUNT(*) AS n
    FROM mv_hot.odoo_order_state_current
    WHERE tenant = 'ucepsa' AND site = 'ucepsa_onpremise'
      AND order_present = true AND order_coherent = true
), cycle_missing AS (
    SELECT COUNT(*) AS n
    FROM mv_hot.odoo_order_state_current o
    WHERE o.tenant = 'ucepsa' AND o.site = 'ucepsa_onpremise'
      AND o.order_present = true AND o.order_coherent = true
      AND NOT EXISTS (
          SELECT 1
          FROM mv_loss_intelligence.product_cycle_parameters p
          WHERE p.tenant = o.tenant
            AND p.site = o.site
            AND p.product_id = o.product_id
            AND p.is_active = true
            AND (p.machine_id IS NULL OR p.machine_id = o.machine_id)
      )
), stale_open AS (
    SELECT COUNT(*) AS n
    FROM mv_hot.edge_oee_machine_stops
    WHERE tenant = 'ucepsa' AND site = 'ucepsa_onpremise'
      AND status = 'OPEN'
      AND started_at < now() - interval '12 hours'
)
SELECT 'DEPLOYMENT_CONFIG'::text AS check_code,
       CASE WHEN n = 1 THEN 'OK' ELSE 'BLOCKER' END AS status,
       n AS affected_count,
       'Debe existir una única configuración CUTTERS.'::text AS description
FROM deployment
UNION ALL
SELECT 'CAPACITY_COST', CASE WHEN n >= 1 THEN 'OK' ELSE 'BLOCKER' END, n,
       'Debe existir coste de capacidad €/h activo.' FROM capacity_cost
UNION ALL
SELECT 'ENERGY_COST', CASE WHEN n >= 1 THEN 'OK' ELSE 'BLOCKER' END, n,
       'Debe existir coste eléctrico variable €/kWh activo.' FROM energy_cost
UNION ALL
SELECT 'CURRENT_VALID_ORDERS', CASE WHEN n >= 1 THEN 'OK' ELSE 'INFO' END, n,
       'Órdenes actualmente cargadas y coherentes.' FROM current_loaded
UNION ALL
SELECT 'MISSING_CYCLE_PARAMETERS', CASE WHEN n = 0 THEN 'OK' ELSE 'WARNING' END, n,
       'Productos activos sin bolsas por golpe o cadencia configurada.' FROM cycle_missing
UNION ALL
SELECT 'STALE_OPEN_STOPS', CASE WHEN n = 0 THEN 'OK' ELSE 'WARNING' END, n,
       'Paros abiertos desde hace más de 12 horas; no equivalen automáticamente a pérdida.' FROM stale_open;

DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'grafana_ucepsa_ro') THEN
        GRANT SELECT ON mv_reports_ucepsa_prod.li_stop_candidates_v1 TO grafana_ucepsa_ro;
        GRANT SELECT ON mv_reports_ucepsa_prod.li_stop_groups_v1 TO grafana_ucepsa_ro;
        GRANT SELECT ON mv_reports_ucepsa_prod.li_shadow_review_queue_v1 TO grafana_ucepsa_ro;
        GRANT SELECT ON mv_reports_ucepsa_prod.li_operator_stop_queue_v1 TO grafana_ucepsa_ro;
        GRANT SELECT ON mv_reports_ucepsa_prod.li_daily_review_queue_v1 TO grafana_ucepsa_ro;
        GRANT SELECT ON mv_reports_ucepsa_prod.li_shadow_summary_v1 TO grafana_ucepsa_ro;
        GRANT SELECT ON mv_reports_ucepsa_prod.li_product_cycle_coverage_v1 TO grafana_ucepsa_ro;
        GRANT SELECT ON mv_reports_ucepsa_prod.li_data_quality_v1 TO grafana_ucepsa_ro;
    END IF;
END $$;

COMMIT;
