import { describe, expect, it } from 'vitest';

import { buildTrackAdjacency, computeRoute } from './route';
import type { Station, Track } from '../types/domain';

const baseTimestamps = {
  createdAt: '2024-01-01T00:00:00Z',
  updatedAt: '2024-01-01T00:00:00Z',
};

describe('route utilities', () => {
  it('finds a multi-hop path across connected tracks', () => {
    const stations: Station[] = [
      {
        id: 'station-a',
        name: 'Alpha',
        latitude: 51.5,
        longitude: -0.1,
        ...baseTimestamps,
      },
      {
        id: 'station-b',
        name: 'Bravo',
        latitude: 51.52,
        longitude: -0.11,
        ...baseTimestamps,
      },
      {
        id: 'station-c',
        name: 'Charlie',
        latitude: 51.54,
        longitude: -0.12,
        ...baseTimestamps,
      },
      {
        id: 'station-d',
        name: 'Delta',
        latitude: 51.55,
        longitude: -0.15,
        ...baseTimestamps,
      },
    ];

    const tracks: Track[] = [
      {
        id: 'track-ab',
        startStationId: 'station-a',
        endStationId: 'station-b',
        lengthMeters: 1200,
        maxSpeedKph: 120,
        coordinates: [
          [51.5, -0.1],
          [51.51, -0.105],
          [51.52, -0.11],
        ],
        ...baseTimestamps,
      },
      {
        id: 'track-bc',
        startStationId: 'station-b',
        endStationId: 'station-c',
        lengthMeters: 1500,
        maxSpeedKph: 110,
        coordinates: [
          [51.52, -0.11],
          [51.53, -0.115],
          [51.54, -0.12],
        ],
        ...baseTimestamps,
      },
      {
        id: 'track-cd',
        startStationId: 'station-c',
        endStationId: 'station-d',
        lengthMeters: 900,
        maxSpeedKph: 115,
        coordinates: [
          [51.54, -0.12],
          [51.545, -0.13],
          [51.55, -0.15],
        ],
        ...baseTimestamps,
      },
    ];

    const stationById = new Map(stations.map((station) => [station.id, station]));
    const adjacency = buildTrackAdjacency(tracks);

    const result = computeRoute({
      startId: 'station-a',
      endId: 'station-d',
      stationById,
      adjacency,
    });

    expect(result.error).toBeNull();
    expect(result.stations?.map((station) => station.id)).toEqual([
      'station-a',
      'station-b',
      'station-c',
      'station-d',
    ]);
    expect(result.tracks.map((track) => track.id)).toEqual([
      'track-ab',
      'track-bc',
      'track-cd',
    ]);
    expect(result.totalLength).toBe(1200 + 1500 + 900);
    expect(result.segments).toHaveLength(3);
    expect(result.segments[0][0]).toEqual([51.5, -0.1]);
    expect(result.segments[2][result.segments[2].length - 1]).toEqual([51.55, -0.15]);
  });

  it('returns an error when no path exists', () => {
    const stations: Station[] = [
      {
        id: 'station-a',
        name: 'Alpha',
        latitude: 51.5,
        longitude: -0.1,
        ...baseTimestamps,
      },
      {
        id: 'station-b',
        name: 'Bravo',
        latitude: 51.6,
        longitude: -0.2,
        ...baseTimestamps,
      },
    ];

    const tracks: Track[] = [
      {
        id: 'track-self',
        startStationId: 'station-a',
        endStationId: 'station-a',
        lengthMeters: 0,
        maxSpeedKph: 80,
        coordinates: [
          [51.5, -0.1],
          [51.5005, -0.1005],
        ],
        ...baseTimestamps,
      },
    ];

    const stationById = new Map(stations.map((station) => [station.id, station]));
    const adjacency = buildTrackAdjacency(tracks);

    const result = computeRoute({
      startId: 'station-a',
      endId: 'station-b',
      stationById,
      adjacency,
    });

    expect(result.stations).toBeNull();
    expect(result.tracks).toHaveLength(0);
    expect(result.error).toBe(
      'No rail connection found between the selected stations.'
    );
    expect(result.segments).toHaveLength(0);
  });

  it('reverses track geometry when traversing in the opposite direction', () => {
    const stations: Station[] = [
      {
        id: 'station-a',
        name: 'Alpha',
        latitude: 51.5,
        longitude: -0.1,
        ...baseTimestamps,
      },
      {
        id: 'station-b',
        name: 'Bravo',
        latitude: 51.52,
        longitude: -0.11,
        ...baseTimestamps,
      },
    ];

    const tracks: Track[] = [
      {
        id: 'track-ab',
        startStationId: 'station-a',
        endStationId: 'station-b',
        lengthMeters: 1200,
        maxSpeedKph: 120,
        coordinates: [
          [51.5, -0.1],
          [51.52, -0.11],
        ],
        ...baseTimestamps,
      },
    ];

    const stationById = new Map(stations.map((station) => [station.id, station]));
    const adjacency = buildTrackAdjacency(tracks);

    const result = computeRoute({
      startId: 'station-b',
      endId: 'station-a',
      stationById,
      adjacency,
    });

    expect(result.error).toBeNull();
    expect(result.segments).toEqual([
      [
        [51.52, -0.11],
        [51.5, -0.1],
      ],
    ]);
  });
});
