import { DataSource } from 'typeorm';
import { seedVisibleVerticalSlice } from './vertical-slice.seed';

type Row = Record<string, unknown>;

const DEMO_CHARACTER_ID = '10000000-0000-4000-8000-000000000001';
const SOUTH_GATE_ID = '20000000-0000-4000-8000-000000000001';
const BURNED_ROAD_ID = '20000000-0000-4000-8000-000000000002';

class InMemoryRepository {
  readonly rows: Row[] = [];
  readonly upsert = jest.fn(
    async (
      values: Row | Row[],
      conflictPaths: string[] | { conflictPaths: string[] },
    ) => {
      const conflictKeys = Array.isArray(conflictPaths)
        ? conflictPaths
        : conflictPaths.conflictPaths;

      for (const value of Array.isArray(values) ? values : [values]) {
        const existing = this.rows.find((row) =>
          conflictKeys.every((key) => row[key] === value[key]),
        );

        if (existing) {
          Object.assign(existing, value);
        } else {
          this.rows.push({ ...value });
        }
      }
    },
  );
  readonly findOneBy = jest.fn(async (criteria: Row) =>
    this.rows.find((row) =>
      Object.entries(criteria).every(([key, value]) => row[key] === value),
    ),
  );
  readonly insert = jest.fn(async (value: Row) => {
    this.rows.push({ ...value });
  });
}

describe('seedVisibleVerticalSlice', () => {
  it('upserts the two locations and directed connections while inserting one stable demo character', async () => {
    const locationRepository = new InMemoryRepository();
    const connectionRepository = new InMemoryRepository();
    const characterRepository = new InMemoryRepository();
    const dataSource = {
      getRepository: jest
        .fn()
        .mockReturnValueOnce(locationRepository)
        .mockReturnValueOnce(connectionRepository)
        .mockReturnValueOnce(characterRepository)
        .mockReturnValueOnce(locationRepository)
        .mockReturnValueOnce(connectionRepository)
        .mockReturnValueOnce(characterRepository),
    } as unknown as DataSource;

    await seedVisibleVerticalSlice(dataSource);
    await seedVisibleVerticalSlice(dataSource);

    expect(locationRepository.upsert).toHaveBeenCalledWith(
      expect.arrayContaining([
        expect.objectContaining({ id: SOUTH_GATE_ID, key: 'south-gate' }),
        expect.objectContaining({ id: BURNED_ROAD_ID, key: 'burned-road' }),
      ]),
      ['key'],
    );
    expect(connectionRepository.upsert).toHaveBeenCalledWith(
      expect.arrayContaining([
        expect.objectContaining({
          fromLocationId: SOUTH_GATE_ID,
          toLocationId: BURNED_ROAD_ID,
        }),
        expect.objectContaining({
          fromLocationId: BURNED_ROAD_ID,
          toLocationId: SOUTH_GATE_ID,
        }),
      ]),
      ['fromLocationId', 'toLocationId'],
    );
    expect(characterRepository.insert).toHaveBeenCalledWith(
      expect.objectContaining({ id: DEMO_CHARACTER_ID }),
    );
    expect(locationRepository.rows).toHaveLength(2);
    expect(connectionRepository.rows).toHaveLength(2);
    expect(characterRepository.rows).toHaveLength(1);
  });
});
