import { NotFoundException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { Character } from '../characters/entities/character.entity';
import {
  BURNED_ROAD_ID,
  SOUTH_GATE_ID,
} from '../database/seeds/vertical-slice.constants';
import { TravelService } from '../travel/travel.service';
import { LocationConnection } from './entities/location-connection.entity';
import { LocationDefinition } from './entities/location-definition.entity';
import { WorldService } from './world.service';

const CHARACTER_ID = '10000000-0000-4000-8000-000000000001';

function currentLocation(): LocationDefinition {
  return {
    id: SOUTH_GATE_ID,
    key: 'south-gate',
    name: 'S\u00fcdtor von Graufurt',
    description:
      'Das S\u00fcdtor ist der sichere Ausgangspunkt nach S\u00fcden.',
    regionKey: 'ashen-fields',
    minRecommendedLevel: 1,
    maxRecommendedLevel: 1,
    dangerLevel: 0,
    isSafe: true,
    huntingEnabled: false,
    artworkPath: '/assets/locations/south-gate.webp',
    createdAt: new Date('2026-08-18T09:00:00.000Z'),
    updatedAt: new Date('2026-08-18T09:00:00.000Z'),
    characters: [],
    outgoingConnections: [],
    incomingConnections: [],
  };
}

function burnedRoad(): LocationDefinition {
  return {
    id: BURNED_ROAD_ID,
    key: 'burned-road',
    name: 'Verbrannte Stra\u00dfe',
    description: 'Eine verbrannte Handelsroute durch die Aschenfelder.',
    regionKey: 'ashen-fields',
    minRecommendedLevel: 1,
    maxRecommendedLevel: 2,
    dangerLevel: 1,
    isSafe: false,
    huntingEnabled: true,
    artworkPath: '/assets/locations/burned-road.webp',
    createdAt: new Date('2026-08-18T09:00:00.000Z'),
    updatedAt: new Date('2026-08-18T09:00:00.000Z'),
    characters: [],
    outgoingConnections: [],
    incomingConnections: [],
  };
}

describe('WorldService', () => {
  it('returns the authoritative current location and only enabled public connections', async () => {
    const callOrder: string[] = [];
    const location = currentLocation();
    const completeTravelIfDue = jest.fn().mockImplementation(() => {
      callOrder.push('completeTravelIfDue');
      return Promise.resolve({ status: 'IDLE' });
    });
    const travelService = {
      completeTravelIfDue,
    } as unknown as TravelService;
    const findCharacter = jest.fn().mockImplementation(() => {
      callOrder.push('findCharacter');
      return Promise.resolve({
        id: CHARACTER_ID,
        currentLocationId: SOUTH_GATE_ID,
        currentLocation: location,
      });
    });
    const characters = {
      findOne: findCharacter,
    } as unknown as Repository<Character>;
    const findConnections = jest.fn().mockResolvedValue([
      {
        id: '30000000-0000-4000-8000-000000000001',
        fromLocationId: SOUTH_GATE_ID,
        toLocationId: BURNED_ROAD_ID,
        travelDurationSeconds: 10,
        ambushChance: '0.0500',
        enabled: true,
        fromLocation: location,
        toLocation: burnedRoad(),
      },
      {
        id: '30000000-0000-4000-8000-000000000002',
        fromLocationId: SOUTH_GATE_ID,
        toLocationId: '20000000-0000-4000-8000-000000000003',
        travelDurationSeconds: 30,
        ambushChance: '0.9000',
        enabled: false,
        fromLocation: location,
        toLocation: burnedRoad(),
      },
    ]);
    const connections = {
      find: findConnections,
    } as unknown as Repository<LocationConnection>;
    const service = new WorldService(travelService, characters, connections);

    const result = await service.getCurrentLocation(CHARACTER_ID);

    expect(callOrder).toEqual(['completeTravelIfDue', 'findCharacter']);
    expect(result).toEqual({
      id: SOUTH_GATE_ID,
      key: 'south-gate',
      name: 'S\u00fcdtor von Graufurt',
      description:
        'Das S\u00fcdtor ist der sichere Ausgangspunkt nach S\u00fcden.',
      regionKey: 'ashen-fields',
      minRecommendedLevel: 1,
      maxRecommendedLevel: 1,
      dangerLevel: 0,
      isSafe: true,
      huntingEnabled: false,
      artworkPath: '/assets/locations/south-gate.webp',
      connections: [
        {
          targetLocation: {
            id: BURNED_ROAD_ID,
            key: 'burned-road',
            name: 'Verbrannte Stra\u00dfe',
          },
          travelDurationSeconds: 10,
          danger: 'LOW',
        },
      ],
    });
    expect(findCharacter).toHaveBeenCalledWith({
      where: { id: CHARACTER_ID },
      relations: { currentLocation: true },
    });
    expect(findConnections).toHaveBeenCalledWith({
      where: { fromLocationId: SOUTH_GATE_ID, enabled: true },
      relations: { toLocation: true },
    });
  });

  it('reports a missing character after travel completion', async () => {
    const completeTravelIfDue = jest.fn().mockResolvedValue({
      status: 'IDLE',
    });
    const travelService = {
      completeTravelIfDue,
    } as unknown as TravelService;
    const findCharacter = jest.fn().mockResolvedValue(null);
    const characters = {
      findOne: findCharacter,
    } as unknown as Repository<Character>;
    const findConnections = jest.fn();
    const connections = {
      find: findConnections,
    } as unknown as Repository<LocationConnection>;
    const service = new WorldService(travelService, characters, connections);

    await expect(
      service.getCurrentLocation(CHARACTER_ID),
    ).rejects.toBeInstanceOf(NotFoundException);
    expect(findConnections).not.toHaveBeenCalled();
  });
});
