import { describe, expect, it } from 'vitest';
import {
    getCityDistance,
    isNeighbor,
    searchAllDistanceByCityList,
    searchAllDistanceByNationList,
    searchDistance,
    searchDistanceEntries,
} from '@sammo-ts/logic/world/distance.js';
import type { City } from '@sammo-ts/logic';
import type { MapDefinition } from '@sammo-ts/logic/world/types.js';

describe('World Distance', () => {
    const mockMap: MapDefinition = {
        id: 'test_map',
        name: 'Test Map',
        cities: [
            { id: 1, connections: [2, 3] },
            { id: 2, connections: [1, 4] },
            { id: 3, connections: [1, 5] },
            { id: 4, connections: [2, 6] },
            { id: 5, connections: [3, 7] },
            { id: 6, connections: [4] },
            { id: 7, connections: [5] },
            // Isolated city
            { id: 8, connections: [] },
        ] as any[],
    };

    describe('getCityDistance', () => {
        it('should return 0 for same city', () => {
            expect(getCityDistance(mockMap, 1, 1)).to.equal(0);
        });

        it('should return 1 for adjacent cities', () => {
            expect(getCityDistance(mockMap, 1, 2)).to.equal(1);
            expect(getCityDistance(mockMap, 1, 3)).to.equal(1);
        });

        it('should return correct distance for distant cities', () => {
            expect(getCityDistance(mockMap, 1, 4)).to.equal(2); // 1-2-4
            expect(getCityDistance(mockMap, 1, 6)).to.equal(3); // 1-2-4-6
        });

        it('should return Infinity for unreachable cities', () => {
            expect(getCityDistance(mockMap, 1, 8)).to.equal(Infinity);
        });
    });

    describe('searchDistance', () => {
        it('should include start city with distance 0', () => {
            const result = searchDistance(mockMap, 1, 0);
            expect(result).to.deep.equal({ 1: 0 });
        });

        it('should find cities within range 1', () => {
            const result = searchDistance(mockMap, 1, 1);
            expect(result).to.deep.equal({
                1: 0,
                2: 1,
                3: 1,
            });
        });

        it('should find cities within range 2', () => {
            const result = searchDistance(mockMap, 1, 2);
            expect(result).to.deep.equal({
                1: 0,
                2: 1,
                3: 1,
                4: 2,
                5: 2,
            });
        });

        it('should not include unreachable cities', () => {
            const result = searchDistance(mockMap, 1, 10);
            expect(result).not.to.have.property('8');
        });

        it('preserves legacy BFS visit order independently of numeric object-key ordering', () => {
            const orderMap: MapDefinition = {
                id: 'order-map',
                name: 'Order Map',
                cities: [
                    { id: 1, connections: [10, 2] },
                    { id: 10, connections: [1] },
                    { id: 2, connections: [1] },
                ] as any[],
            };
            expect(searchDistanceEntries(orderMap, 1, 1)).toEqual([
                [1, 0],
                [10, 1],
                [2, 1],
            ]);
        });
    });

    describe('AI distance projections', () => {
        const cities = [
            { id: 1, nationId: 1, supplyState: 1 },
            { id: 2, nationId: 2, supplyState: 1 },
            { id: 3, nationId: 1, supplyState: 0 },
        ] as City[];

        it('projects pairwise distances only across the selected city set', () => {
            expect(searchAllDistanceByCityList(mockMap, [1, 2, 4])).toEqual({
                1: { 1: 0, 2: 1, 4: 2 },
                2: { 1: 1, 2: 0, 4: 1 },
                4: { 1: 2, 2: 1, 4: 0 },
            });
            expect(searchAllDistanceByNationList(mockMap, cities, [1], true)).toEqual({ 1: { 1: 0 } });
        });

        it('checks supplied and unsupplied borders with the requested policy', () => {
            expect(isNeighbor(mockMap, cities, 1, 2, true)).toBe(true);
            expect(isNeighbor(mockMap, cities, 1, 2, false)).toBe(true);
            expect(isNeighbor(mockMap, cities, 1, 1, true)).toBe(false);
        });
    });
});
