import assert from 'node:assert';
import { describe, it } from 'node:test';

import { Stop } from '../../stops/stops.js';
import { timeFromHMS } from '../../timetable/time.js';
import { Route, ServiceRouteInfo, Transfer, VehicleLeg } from '../route.js';

describe('Route', () => {
  const stopA: Stop = {
    id: 1,
    sourceStopId: 'A',
    name: 'Stop A',
    locationType: 'SIMPLE_STOP_OR_PLATFORM',
    children: [],
  };

  const stopB: Stop = {
    id: 2,
    sourceStopId: 'B',
    name: 'Stop B',
    locationType: 'SIMPLE_STOP_OR_PLATFORM',
    children: [],
  };

  const stopC: Stop = {
    id: 3,
    sourceStopId: 'C',
    name: 'Stop C',
    locationType: 'SIMPLE_STOP_OR_PLATFORM',
    children: [],
  };

  const stopD: Stop = {
    id: 4,
    sourceStopId: 'D',
    name: 'Stop D',
    locationType: 'SIMPLE_STOP_OR_PLATFORM',
    children: [],
  };

  const serviceRoute: ServiceRouteInfo = {
    type: 'BUS',
    name: 'Route 1',
  };

  const serviceRoute2: ServiceRouteInfo = {
    type: 'RAIL',
    name: 'Route 2',
  };

  const vehicleLeg: VehicleLeg = {
    from: stopA,
    to: stopB,
    route: serviceRoute,
    departureTime: timeFromHMS(8, 0, 0),
    arrivalTime: timeFromHMS(8, 30, 0),
    pickUpType: 'REGULAR',
    dropOffType: 'REGULAR',
  };

  const transferLeg: Transfer = {
    from: stopB,
    to: stopC,
    type: 'RECOMMENDED',
    minTransferTime: 5,
  };

  const secondVehicleLeg: VehicleLeg = {
    from: stopC,
    to: stopD,
    route: serviceRoute2,
    departureTime: timeFromHMS(8, 40, 0),
    arrivalTime: timeFromHMS(9, 0, 0),
    pickUpType: 'REGULAR',
    dropOffType: 'REGULAR',
  };

  it('should calculate the correct departure time', () => {
    const route = new Route([vehicleLeg, transferLeg, secondVehicleLeg]);
    const departureTime = route.departureTime();
    assert.strictEqual(departureTime, timeFromHMS(8, 0, 0));
  });

  it('should calculate the correct arrival time', () => {
    const route = new Route([vehicleLeg, transferLeg, secondVehicleLeg]);
    const arrivalTime = route.arrivalTime();
    assert.strictEqual(arrivalTime, timeFromHMS(9, 0, 0));
  });

  it('should calculate the total duration of the route', () => {
    const route = new Route([vehicleLeg, transferLeg, secondVehicleLeg]);
    const totalDuration = route.totalDuration();
    assert.strictEqual(totalDuration, 60);
  });

  it('should throw an error if no vehicle leg is found for departure time', () => {
    const route = new Route([transferLeg]);
    assert.throws(() => route.departureTime(), /No vehicle leg found in route/);
  });

  it('should throw an error if no vehicle leg is found for arrival time', () => {
    const route = new Route([transferLeg]);
    assert.throws(() => route.arrivalTime(), /No vehicle leg found in route/);
  });

  describe('toString', () => {
    it('includes leg numbers, stop names, and travel details for each leg', () => {
      const route = new Route([vehicleLeg, transferLeg, secondVehicleLeg]);
      const str = route.toString();
      assert(str.includes('Leg 1:'));
      assert(str.includes('Leg 2:'));
      assert(str.includes('Leg 3:'));
      assert(str.includes('Stop A'));
      assert(str.includes('Stop D'));
      assert(str.includes('BUS Route 1'));
      assert(str.includes('RAIL Route 2'));
      assert(str.includes('Transfer: RECOMMENDED'));
    });

    it('includes platform info when present', () => {
      const stopWithPlatform: Stop = {
        ...stopA,
        platform: '3A',
      };
      const legWithPlatform: VehicleLeg = {
        ...vehicleLeg,
        from: stopWithPlatform,
      };
      const route = new Route([legWithPlatform]);
      assert(route.toString().includes('Pl. 3A'));
    });
  });
});
