-- migrate:up
--
-- Full rollback of the CSV import (migrations 15 + 16 + the data mutations
-- performed by scripts/import_csv_roster.py). After this runs, the database
-- is back to the post-migration-14 shape: ~144 vehicles split per device_name
-- (preserving _Track / _CAM suffix vehicles), no CSV-only columns, and
-- serve.fn_live_view back to its v3 body (driver_name from heuristic only).
--
-- Why a single forward migration instead of `dbmate rollback`:
--   - The CSV import mutated row state, not just schema. A schema-only down
--     would leave 124 merged vehicles in place.
--   - dbmate rollback runs the down blocks in reverse, but mig 16's down only
--     drops the function — it doesn't restore the previous body. Doing it
--     forward lets us be explicit about the target state.

-- ---------------------------------------------------------------------------
-- 1. Re-split vehicles by parsed plate from device_name
-- ---------------------------------------------------------------------------

DO $rollback$
DECLARE
    rec RECORD;
    new_vid integer;
BEGIN
    ALTER TABLE domain.vehicles DROP CONSTRAINT IF EXISTS vehicles_plate_key;

    FOR rec IN
        SELECT
            d.imei,
            d.vehicle_id AS current_vid,
            v.plate      AS current_plate,
            regexp_replace(
                (regexp_match(lp.device_name, '^.* - (.+)$'))[1],
                '_(cam|CAM)$', ''
            ) AS parsed_plate
          FROM domain.devices d
          JOIN state.live_positions lp ON lp.imei = d.imei
          JOIN domain.vehicles v       ON v.vehicle_id = d.vehicle_id
         WHERE lp.device_name LIKE '% - %'
    LOOP
        IF rec.parsed_plate IS NULL OR rec.parsed_plate = '' THEN CONTINUE; END IF;
        IF rec.parsed_plate !~ '[A-Z]' OR rec.parsed_plate !~ '[0-9]' THEN CONTINUE; END IF;
        IF rec.parsed_plate = rec.current_plate THEN CONTINUE; END IF;

        SELECT vehicle_id INTO new_vid
          FROM domain.vehicles
         WHERE plate = rec.parsed_plate;

        IF new_vid IS NULL THEN
            INSERT INTO domain.vehicles (plate)
            VALUES (rec.parsed_plate)
            RETURNING vehicle_id INTO new_vid;
        END IF;

        UPDATE domain.devices         SET vehicle_id = new_vid WHERE imei = rec.imei;
        UPDATE state.live_positions   SET vehicle_id = new_vid WHERE imei = rec.imei;
        UPDATE state.position_history SET vehicle_id = new_vid WHERE imei = rec.imei;
    END LOOP;

    -- Delete vehicles that no device or state row references anymore
    DELETE FROM domain.vehicles v
    WHERE NOT EXISTS (SELECT 1 FROM domain.devices d         WHERE d.vehicle_id = v.vehicle_id)
      AND NOT EXISTS (SELECT 1 FROM state.live_positions lp  WHERE lp.vehicle_id = v.vehicle_id)
      AND NOT EXISTS (SELECT 1 FROM state.position_history ph WHERE ph.vehicle_id = v.vehicle_id);

    ALTER TABLE domain.vehicles ADD CONSTRAINT vehicles_plate_key UNIQUE (plate);
END
$rollback$;

-- ---------------------------------------------------------------------------
-- 2. Restore serve.fn_live_view to the v3 body (driver_name heuristic only)
-- ---------------------------------------------------------------------------

DROP FUNCTION IF EXISTS serve.fn_live_view(jsonb);

CREATE OR REPLACE FUNCTION serve.fn_live_view(filters jsonb)
RETURNS jsonb
LANGUAGE plpgsql STABLE
AS $$
DECLARE
    fresh_window interval := COALESCE((filters->>'fresh_window')::interval, interval '24 hours');
    offline_after interval := COALESCE((filters->>'offline_after')::interval, interval '5 minutes');
    move_speed_kmh numeric := COALESCE((filters->>'move_speed_kmh')::numeric, 5);
    p_cost_centre text := filters->>'cost_centre';
    p_assigned_city text := filters->>'assigned_city';
    p_vehicle_numbers text[] := CASE
        WHEN filters ? 'vehicle_numbers'
        THEN ARRAY(SELECT jsonb_array_elements_text(filters->'vehicle_numbers'))
        ELSE NULL
    END;
    result jsonb;
BEGIN
    WITH candidates AS (
        SELECT
            lp.imei, lp.occurred_at, lp.geom, lp.speed_kmh, lp.direction_deg,
            lp.mc_type, lp.current_mileage_km, lp.gps_signal, lp.satellites,
            lp.device_name, lp.pos_type,
            d.device_type, d.activation_at,
            v.vehicle_id, v.plate, v.cost_centre, v.assigned_city
          FROM state.live_positions lp
          JOIN domain.devices d  ON d.imei = lp.imei
          JOIN domain.vehicles v ON v.vehicle_id = d.vehicle_id
         WHERE d.lifecycle = 'active'
           AND (p_cost_centre   IS NULL OR v.cost_centre   = p_cost_centre)
           AND (p_assigned_city IS NULL OR v.assigned_city = p_assigned_city)
           AND (p_vehicle_numbers IS NULL OR v.plate = ANY (p_vehicle_numbers))
    ),
    ranked AS (
        SELECT c.*, ROW_NUMBER() OVER (
                PARTITION BY c.vehicle_id
                ORDER BY
                    CASE c.device_type WHEN 'tracker' THEN 0 ELSE 1 END,
                    CASE WHEN c.occurred_at > now() - fresh_window THEN 0 ELSE 1 END,
                    c.occurred_at DESC,
                    c.activation_at DESC NULLS LAST
            ) AS rn
          FROM candidates c
    ),
    deduped AS (SELECT * FROM ranked WHERE rn = 1),
    enriched AS (
        SELECT d.*,
            CASE
                WHEN d.occurred_at <= now() - offline_after THEN 'offline'
                WHEN d.speed_kmh IS NOT NULL AND d.speed_kmh > move_speed_kmh THEN 'moving'
                ELSE 'parked'
            END AS operational_state,
            serve._cost_centre_color(d.cost_centre) AS cost_centre_color,
            EXTRACT(EPOCH FROM (now() - d.occurred_at))::int AS age_sec,
            round(ST_Y(d.geom)::numeric, 4) AS lat_rounded,
            round(ST_X(d.geom)::numeric, 4) AS lng_rounded
          FROM deduped d
    ),
    with_addr AS (
        SELECT e.*, g.address, g.address_short
          FROM enriched e
          LEFT JOIN state.geocoded_positions g
                 ON g.lat_rounded = e.lat_rounded
                AND g.lng_rounded = e.lng_rounded
    ),
    summary AS (
        SELECT jsonb_build_object(
            'total_active', count(*),
            'moving',       count(*) FILTER (WHERE operational_state = 'moving'),
            'parked',       count(*) FILTER (WHERE operational_state = 'parked'),
            'offline',      count(*) FILTER (WHERE operational_state = 'offline'),
            'below_freshness_slo', count(*) FILTER (
                WHERE occurred_at <= now() - interval '90 seconds'
            ),
            'as_of', to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')
        ) AS s
          FROM with_addr
    ),
    features AS (
        SELECT COALESCE(jsonb_agg(
            jsonb_build_object(
                'type', 'Feature',
                'geometry', ST_AsGeoJSON(e.geom)::jsonb,
                'properties', jsonb_build_object(
                    'vehicle_id',         e.vehicle_id,
                    'plate',              e.plate,
                    'plate_short',        serve._label_short(e.device_name, e.plate),
                    'driver_name',        serve._driver_name(e.device_name),
                    'imei',               e.imei,
                    'device_type',        e.device_type,
                    'device_name',        e.device_name,
                    'mc_type',            e.mc_type,
                    'pos_type',           e.pos_type,
                    'cost_centre',        e.cost_centre,
                    'cost_centre_color',  e.cost_centre_color,
                    'assigned_city',      e.assigned_city,
                    'address',            e.address,
                    'address_short',      e.address_short,
                    'occurred_at',        to_char(e.occurred_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
                    'age_sec',            e.age_sec,
                    'speed_kmh',          e.speed_kmh,
                    'heading_deg',        e.direction_deg,
                    'gps_signal',         e.gps_signal,
                    'satellites',         e.satellites,
                    'current_mileage_km', e.current_mileage_km,
                    'operational_state',  e.operational_state,
                    'style_class',        'vehicle-' || e.operational_state,
                    'marker_color',       CASE WHEN e.operational_state = 'moving'
                                               THEN e.cost_centre_color
                                               ELSE '#9ca3af' END,
                    'show_arrow',         (e.operational_state = 'moving' AND e.direction_deg IS NOT NULL)
                )
            )
        ), '[]'::jsonb) AS feats
          FROM with_addr e
    ),
    slo_block AS (
        SELECT COALESCE(jsonb_object_agg(
            metric,
            jsonb_build_object('threshold', threshold, 'current', current_value, 'status', status)
        ), '{}'::jsonb) AS ss
          FROM slo.v_current_status
    )
    SELECT jsonb_build_object(
        'summary', (SELECT s FROM summary),
        'geojson', jsonb_build_object('type', 'FeatureCollection', 'features', (SELECT feats FROM features)),
        'slo_status', (SELECT ss FROM slo_block)
    )
      INTO result;
    RETURN result;
END;
$$;

-- ---------------------------------------------------------------------------
-- 3. Drop the CSV-only columns from domain.devices
-- ---------------------------------------------------------------------------

ALTER TABLE domain.devices
    DROP COLUMN IF EXISTS driver_name,
    DROP COLUMN IF EXISTS driver_phone,
    DROP COLUMN IF EXISTS iccid,
    DROP COLUMN IF EXISTS expiration_at,
    DROP COLUMN IF EXISTS device_group,
    DROP COLUMN IF EXISTS roster_synced_at;

-- ---------------------------------------------------------------------------
-- 4. Remove the dead schema_migrations rows for the deleted migrations
-- ---------------------------------------------------------------------------
--
-- 20260601000015 (devices_csv_columns) and 20260601000016 (prefer_csv_driver)
-- have been deleted from db/migrations/ — drop their tracker rows so dbmate
-- status stays consistent with the files on disk.

DELETE FROM public.schema_migrations
 WHERE version IN ('20260601000015', '20260601000016');

-- ---------------------------------------------------------------------------
-- 5. Sanity check — log the resulting counts
-- ---------------------------------------------------------------------------

DO $check$
DECLARE
    v_count integer;
    d_count integer;
BEGIN
    SELECT count(*) INTO v_count FROM domain.vehicles;
    SELECT count(*) INTO d_count FROM domain.devices;
    RAISE NOTICE 'rollback complete: % vehicles, % devices', v_count, d_count;
END
$check$;

-- migrate:down
-- No-op: this migration is itself a rollback. To re-apply the CSV import,
-- re-run migrations 15 + 16 and scripts/import_csv_roster.py.
