-- 20_restore_live_feed.sql
-- Restore the live-map feed's intended final state after a migration-ordering regression.
--
-- WHAT HAPPENED: reporting.v_live_positions and reporting.fn_live_positions are first
-- defined in migration 11, then MODIFIED by later migrations:
--   * migration 15 added the cost-centre exclusion to v_live_positions (hide
--     personal/management/mtn from the live map), and
--   * migration 16 added 'vehicle_type'/'fleet_segment' to the fn_live_positions GeoJSON.
-- On 2026-06-10 migration 11 was applied for the first time on prod (the reporting objects
-- had originally been hand-created, so 11 was never recorded; 14/15/16 had already run).
-- Re-running 11 recreated both objects at their BASE definitions, and because 15/16 were
-- already marked applied they were skipped — silently reverting the exclusion and the
-- vehicle_type/fleet_segment additions. Symptom: live-map vehicle count jumped 74 -> 80 and
-- markers lost their specialist-icon fields.
--
-- THE FIX: re-assert both objects' intended final definitions here. As the highest-numbered
-- migration this always runs last, so the correct state wins regardless of apply order.
-- Verbatim union of migration 15 (v_live_positions + exclusion) and migration 16
-- (fn_live_positions + vehicle_type/fleet_segment). Idempotent — safe to re-apply.

SET search_path = tracksolid, reporting, public;

-- ── exclusion config (created by migration 15; re-asserted so this file is self-contained) ──
CREATE TABLE IF NOT EXISTS reporting.map_excluded_cost_centres (
  cost_centre text PRIMARY KEY,            -- compared case-insensitively (store lowercase)
  note        text,
  added_at    timestamptz NOT NULL DEFAULT now()
);

INSERT INTO reporting.map_excluded_cost_centres (cost_centre, note) VALUES
  ('personal',   'staff/personal vehicles — not operational fleet'),
  ('management', 'management vehicles — not operational fleet'),
  ('mtn',        'MTN contract / Uganda (Kampala) — outside Kenyan ops')
ON CONFLICT (cost_centre) DO NOTHING;

-- ── v_live_positions: base definition + cost-centre exclusion (from migration 15) ─────────
CREATE OR REPLACE VIEW reporting.v_live_positions AS
 WITH primary_device AS (
         SELECT DISTINCT ON ((reporting.normalize_plate(d_1.vehicle_number))) reporting.normalize_plate(d_1.vehicle_number) AS vehicle_number,
            d_1.imei AS primary_imei
           FROM devices d_1
             LEFT JOIN live_positions lp_1 ON lp_1.imei = d_1.imei
          WHERE d_1.vehicle_number IS NOT NULL AND d_1.enabled_flag = 1
            -- exclude plates whose device(s) carry a non-operational cost centre
            AND reporting.normalize_plate(d_1.vehicle_number) NOT IN (
                  SELECT reporting.normalize_plate(x.vehicle_number)
                  FROM devices x
                  WHERE x.vehicle_number IS NOT NULL
                    AND lower(trim(x.cost_centre)) IN (
                          SELECT cost_centre FROM reporting.map_excluded_cost_centres)
                )
          ORDER BY (reporting.normalize_plate(d_1.vehicle_number)), (
                CASE
                    WHEN (d_1.mc_type = ANY (ARRAY['GT06E'::text, 'X3'::text, 'AT4'::text])) AND lp_1.gps_time >= (now() - '24:00:00'::interval) THEN 0
                    ELSE 1
                END), lp_1.gps_time DESC NULLS LAST, (
                CASE d_1.mc_type
                    WHEN 'GT06E'::text THEN 1
                    WHEN 'X3'::text THEN 2
                    WHEN 'AT4'::text THEN 3
                    WHEN 'JC400P'::text THEN 4
                    ELSE 5
                END), d_1.activation_time, d_1.imei
        )
 SELECT lp.imei,
    pd.vehicle_number,
    d.driver_name AS assigned_driver,
    d.cost_centre,
    d.assigned_city,
    d.vehicle_category,
    d.vehicle_models,
    d.mc_type,
        CASE d.mc_type
            WHEN 'GT06E'::text THEN 'tracker'::text
            WHEN 'X3'::text THEN 'tracker'::text
            WHEN 'AT4'::text THEN 'tracker'::text
            WHEN 'JC400P'::text THEN 'camera'::text
            ELSE 'other'::text
        END AS device_kind,
    lp.lat,
    lp.lng,
    lp.speed,
    lp.direction,
    lp.acc_status,
    lp.device_status,
    lp.gps_signal,
    lp.gps_num,
    lp.current_mileage,
    lp.loc_desc,
    lp.gps_time,
    lp.updated_at,
    (lp.gps_time AT TIME ZONE 'Africa/Nairobi'::text) AS gps_time_eat,
    (lp.updated_at AT TIME ZONE 'Africa/Nairobi'::text) AS updated_at_eat,
    round(EXTRACT(epoch FROM now() - lp.gps_time) / 3600::numeric, 2) AS source_age_hours
   FROM live_positions lp
     JOIN primary_device pd ON pd.primary_imei = lp.imei
     JOIN devices d ON d.imei = lp.imei;

COMMENT ON TABLE reporting.map_excluded_cost_centres IS
  'Cost centres hidden from the live map (reporting.v_live_positions). Edit to hide/restore; '
  'effective on next query. Seeded: personal, management, mtn. See migration 15.';

-- ── fn_live_positions: base definition + vehicle_type/fleet_segment (from migration 16) ───
CREATE OR REPLACE FUNCTION reporting.fn_live_positions(p_cost_centre text DEFAULT NULL::text, p_acc_status text DEFAULT NULL::text)
 RETURNS jsonb
 LANGUAGE plpgsql
 STABLE
AS $function$
DECLARE
  v_result jsonb;
BEGIN
  p_cost_centre := NULLIF(p_cost_centre, '');
  p_acc_status := NULLIF(p_acc_status, '');
  WITH filtered AS (
    SELECT * FROM reporting.v_live_positions
    WHERE (p_cost_centre IS NULL OR cost_centre = p_cost_centre)
      AND (p_acc_status  IS NULL OR acc_status  = p_acc_status)
  )
  SELECT jsonb_build_object(
    'summary', jsonb_build_object(
      'vehicle_count',       COUNT(*),
      -- "moving" and "parked" both restrict to devices that have reported
      -- within the OFFLINE_THRESHOLD (24 h) so they represent the live
      -- fleet, not equipment-failure stragglers. "offline" is its own
      -- counter for the > 24 h tail.
      'moving',              COUNT(*) FILTER (WHERE acc_status = '1'
                                              AND source_age_hours < 24),
      'parked',              COUNT(*) FILTER (WHERE acc_status = '0'
                                              AND source_age_hours < 24),
      'offline',             COUNT(*) FILTER (WHERE source_age_hours >= 24),
      'median_speed_moving', percentile_cont(0.5) WITHIN GROUP (ORDER BY speed)
                               FILTER (WHERE acc_status = '1'
                                       AND source_age_hours < 24
                                       AND speed > 0),
      'last_batch_at',       to_char(MAX(updated_at) AT TIME ZONE 'Africa/Nairobi',
                                     'YYYY-MM-DD HH24:MI:SS'),
      'oldest_fix_at',       to_char(MIN(gps_time)   AT TIME ZONE 'Africa/Nairobi',
                                     'YYYY-MM-DD HH24:MI:SS'),
      'newest_fix_at',       to_char(MAX(gps_time)   AT TIME ZONE 'Africa/Nairobi',
                                     'YYYY-MM-DD HH24:MI:SS'),
      'last_batch_utc',      MAX(updated_at),
      'newest_fix_utc',      MAX(gps_time)
    ),
    'geojson', jsonb_build_object(
      'type', 'FeatureCollection',
      'features', COALESCE(jsonb_agg(
        jsonb_build_object(
          'type', 'Feature',
          'properties', jsonb_build_object(
            'imei',            imei,
            'vehicle_number',  vehicle_number,
            'driver',          assigned_driver,
            'cost_centre',     cost_centre,
            'assigned_city',   assigned_city,
            'vehicle_category', vehicle_category,
            'vehicle_type',    vehicle_models,
            'fleet_segment',   reporting.fn_fleet_segment(vehicle_models),
            'mc_type',         mc_type,
            'device_kind',     device_kind,
            'source_age_hours', source_age_hours,
            'speed',           speed,
            'direction',       direction,
            'acc_status',      acc_status,
            'device_status',   device_status,
            'gps_signal',      gps_signal,
            'gps_num',         gps_num,
            'current_mileage', current_mileage,
            'loc_desc',        loc_desc,
            'gps_time',        to_char(gps_time   AT TIME ZONE 'Africa/Nairobi',
                                       'YYYY-MM-DD HH24:MI:SS'),
            'updated_at',      to_char(updated_at AT TIME ZONE 'Africa/Nairobi',
                                       'YYYY-MM-DD HH24:MI:SS'),
            'gps_time_utc',    gps_time,
            'updated_at_utc',  updated_at
          ),
          'geometry', jsonb_build_object(
            'type', 'Point',
            'coordinates', jsonb_build_array(lng, lat)
          )
        )
      ), '[]'::jsonb)
    )
  ) INTO v_result FROM filtered;

  RETURN v_result;
END $function$;
