import { ConflictException } from '@nestjs/common';
import type { DataSource } from 'typeorm';

import { RoutesService } from './routes.service';
import type { RoutesRepository } from './routes.repository';

type StopSeq = Array<{ yardId: string; sequenceNo: number }>;

/** DataSource stub whose Route repository returns the given existing routes. */
const serviceWith = (
  existing: Array<{ id: string; milestones: StopSeq }>,
): RoutesService => {
  const dataSource = {
    getRepository: () => ({ find: async () => existing }),
  } as unknown as DataSource;
  return new RoutesService(dataSource, {} as RoutesRepository);
};

const assertNotDuplicate = (
  service: RoutesService,
  yardIds: string[],
  excludeRouteId?: string,
): Promise<void> =>
  (
    service as unknown as {
      assertNotDuplicate: (
        m: Array<{ yardId: string }>,
        id?: string,
      ) => Promise<void>;
    }
  ).assertNotDuplicate(
    yardIds.map((yardId) => ({ yardId })),
    excludeRouteId,
  );

describe('RoutesService duplicate guard', () => {
  const addisAdamaDire: StopSeq = [
    { yardId: 'addis', sequenceNo: 1 },
    { yardId: 'adama', sequenceNo: 2 },
    { yardId: 'dire', sequenceNo: 3 },
  ];

  it('rejects an identical stop sequence', async () => {
    const service = serviceWith([{ id: 'r1', milestones: addisAdamaDire }]);

    await expect(
      assertNotDuplicate(service, ['addis', 'adama', 'dire']),
    ).rejects.toBeInstanceOf(ConflictException);
  });

  it('allows the same endpoints with a different corridor', async () => {
    // Same origin + destination, but skipping Adama is a genuinely other route.
    const service = serviceWith([{ id: 'r1', milestones: addisAdamaDire }]);

    await expect(
      assertNotDuplicate(service, ['addis', 'dire']),
    ).resolves.toBeUndefined();
  });

  it('does not flag the route being edited against itself', async () => {
    const service = serviceWith([{ id: 'r1', milestones: addisAdamaDire }]);

    await expect(
      assertNotDuplicate(service, ['addis', 'adama', 'dire'], 'r1'),
    ).resolves.toBeUndefined();
  });

  it('compares stops by sequence, not storage order', async () => {
    const shuffled: StopSeq = [
      { yardId: 'dire', sequenceNo: 3 },
      { yardId: 'addis', sequenceNo: 1 },
      { yardId: 'adama', sequenceNo: 2 },
    ];
    const service = serviceWith([{ id: 'r1', milestones: shuffled }]);

    await expect(
      assertNotDuplicate(service, ['addis', 'adama', 'dire']),
    ).rejects.toBeInstanceOf(ConflictException);
  });
});
