import { describe, expect, it, vi } from 'vitest';
import type { TurnSchedule } from '@sammo-ts/logic';
import type { TurnGeneral, TurnWorldSnapshot, TurnWorldState } from '../src/turn/types.js';
import { InMemoryTurnWorld } from '../src/turn/inMemoryWorld.js';
import { InMemoryReservedTurnStore } from '../src/turn/reservedTurnStore.js';
import { createReservedTurnHandler } from '../src/turn/reservedTurnHandler.js';
import { InMemoryTurnProcessor } from '../src/turn/inMemoryTurnProcessor.js';

const mockDate = new Date('0189-01-01T00:00:00Z');

const MINIMAL_MAP = {
    id: 'npc_domestic_map',
    name: 'NPC내정맵',
    cities: [
        {
            id: 1,
            name: '소성A',
            level: 1,
            region: 1,
            position: { x: 10, y: 10 },
            connections: [2],
            max: {
                population: 20000,
                agriculture: 2000,
                commerce: 2000,
                security: 2000,
                defence: 1000,
                wall: 1000,
            },
            initial: {
                population: 10000,
                agriculture: 1000,
                commerce: 1000,
                security: 1000,
                defence: 500,
                wall: 500,
            },
        },
        {
            id: 2,
            name: '중성B',
            level: 1,
            region: 2,
            position: { x: 20, y: 10 },
            connections: [1],
            max: {
                population: 20000,
                agriculture: 2000,
                commerce: 2000,
                security: 2000,
                defence: 1000,
                wall: 1000,
            },
            initial: {
                population: 10000,
                agriculture: 1000,
                commerce: 1000,
                security: 1000,
                defence: 500,
                wall: 500,
            },
        },
    ],
    defaults: { trust: 50, trade: 100, supplyState: 1, frontState: 0 },
};

const createMockPrisma = (initialGeneralRows: any[] = []) => {
    let generalRows = [...initialGeneralRows];
    return {
        generalTurn: {
            findMany: vi.fn(async ({ where } = {}) => {
                if (where?.generalId) {
                    return generalRows
                        .filter((row) => row.generalId === where.generalId)
                        .sort((a, b) => a.turnIdx - b.turnIdx);
                }
                return generalRows;
            }),
            deleteMany: vi.fn(async ({ where } = {}) => {
                if (where?.generalId) {
                    generalRows = generalRows.filter((row) => row.generalId !== where.generalId);
                }
                return { count: 0 };
            }),
            createMany: vi.fn(async ({ data }) => {
                if (Array.isArray(data)) {
                    generalRows.push(...data);
                }
                return { count: data.length };
            }),
        },
        nationTurn: {
            findMany: vi.fn(async () => []),
            deleteMany: vi.fn(async () => ({ count: 0 })),
            createMany: vi.fn(async () => ({ count: 0 })),
        },
    };
};

const addMinutes = (time: Date, minutes: number): Date => new Date(time.getTime() + minutes * 60_000);

describe('NPC 일반 내정 턴', () => {
    it('고정 seed에서 예약 턴 없이 치안 명령을 실행하고 다음 턴으로 이동한다', async () => {
        const generals: TurnGeneral[] = [
            {
                id: 1,
                name: 'NPC_무장',
                nationId: 1,
                cityId: 1,
                troopId: 0,
                stats: { leadership: 75, strength: 75, intelligence: 15 },
                turnTime: mockDate,
                role: {
                    items: { horse: null, weapon: null, book: null, item: null },
                    personality: null,
                    specialDomestic: null,
                    specialWar: null,
                },
                triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} },
                meta: { killturn: 999, text: '기부는 저처럼 돈 많은 사람들이 많이 하면 됩니다' },
                officerLevel: 4,
                experience: 0,
                dedication: 0,
                injury: 0,
                gold: 0,
                rice: 0,
                crew: 0,
                crewTypeId: 0,
                train: 0,
                atmos: 0,
                age: 30,
                npcState: 2,
            },
        ];

        const cities = [
            {
                id: 1,
                name: '소성A',
                nationId: 1,
                level: 1,
                state: 0,
                population: 10000,
                populationMax: 20000,
                agriculture: 1000,
                agricultureMax: 2000,
                commerce: 1000,
                commerceMax: 2000,
                security: 1000,
                securityMax: 2000,
                supplyState: 1,
                frontState: 0,
                defence: 500,
                defenceMax: 1000,
                wall: 500,
                wallMax: 1000,
                meta: { trust: 98 },
            },
            {
                id: 2,
                name: '중성B',
                nationId: 2,
                level: 1,
                state: 0,
                population: 12000,
                populationMax: 20000,
                agriculture: 1200,
                agricultureMax: 2000,
                commerce: 1200,
                commerceMax: 2000,
                security: 1200,
                securityMax: 2000,
                supplyState: 1,
                frontState: 0,
                defence: 600,
                defenceMax: 1000,
                wall: 600,
                wallMax: 1000,
                meta: { trust: 98 },
            },
        ];

        const nations = [
            {
                id: 1,
                name: 'NPC국가',
                color: '#FF0000',
                capitalCityId: 1,
                chiefGeneralId: 1,
                gold: 50000,
                rice: 50000,
                power: 0,
                level: 1,
                typeCode: 'che_def',
                meta: {},
            },
            {
                id: 2,
                name: '경쟁국',
                color: '#0000FF',
                capitalCityId: 2,
                chiefGeneralId: null,
                gold: 50000,
                rice: 50000,
                power: 0,
                level: 1,
                typeCode: 'che_def',
                meta: {},
            },
        ];

        const snapshot: TurnWorldSnapshot = {
            generals: generals as any,
            cities: cities as any,
            nations: nations as any,
            troops: [],
            diplomacy: [],
            events: [],
            initialEvents: [],
            map: MINIMAL_MAP as any,
            scenarioConfig: {
                stat: { total: 300, min: 10, max: 100, npcTotal: 150, npcMax: 50, npcMin: 10, chiefMin: 70 },
                iconPath: '',
                map: {},
                const: { npcMessageFreqByDay: 144 },
                environment: { mapName: 'npc_domestic_map', unitSet: 'default' },
            },
            scenarioMeta: {
                startYear: 189,
            } as any,
            unitSet: {} as any,
        };

        const state: TurnWorldState = {
            id: 1,
            currentYear: 189,
            currentMonth: 1,
            tickSeconds: 600,
            lastTurnTime: mockDate,
            meta: { seed: 1 },
        };

        const schedule: TurnSchedule = {
            entries: [{ startMinute: 0, tickMinutes: 10 }],
        };

        const mockPrisma = createMockPrisma();
        const reservedTurnStore = new InMemoryReservedTurnStore(mockPrisma as any, {
            maxGeneralTurns: 10,
            maxNationTurns: 10,
        });
        await reservedTurnStore.loadAll();

        const logicalGameNow = addMinutes(mockDate, 3);
        const wrapper = { world: null as InMemoryTurnWorld | null };
        const handler = await createReservedTurnHandler({
            reservedTurns: reservedTurnStore,
            scenarioConfig: snapshot.scenarioConfig,
            scenarioMeta: snapshot.scenarioMeta,
            map: MINIMAL_MAP as any,
            unitSet: snapshot.unitSet,
            getWorld: () => wrapper.world,
            now: () => logicalGameNow,
        });

        const world = new InMemoryTurnWorld(state, snapshot, {
            schedule,
            generalTurnHandler: handler,
        });
        wrapper.world = world;

        const beforeCity = world.getCityById(1)!;
        const beforeStats = {
            population: beforeCity.population,
            agriculture: beforeCity.agriculture,
            commerce: beforeCity.commerce,
            security: beforeCity.security,
            defence: beforeCity.defence,
            wall: beforeCity.wall,
        };

        const processor = new InMemoryTurnProcessor(world, { tickMinutes: 10 });
        await processor.run(addMinutes(mockDate, 10), {
            budgetMs: 1000,
            maxGenerals: 10,
            catchUpCap: 1,
        });

        const afterCity = world.getCityById(1)!;
        expect(afterCity).toMatchObject({
            ...beforeStats,
            // 레거시 내정 수식과 고정 seed의 치명/실패 배율을 적용한 값.
            security: 1063,
        });
        expect(world.getGeneralById(1)!.turnTime.getTime()).toBe(addMinutes(mockDate, 10).getTime());
        expect(world.peekDirtyState().messages).toContainEqual(
            expect.objectContaining({
                msgType: 'public',
                text: '기부는 저처럼 돈 많은 사람들이 많이 하면 됩니다',
                src: expect.objectContaining({ generalId: 1, generalName: 'NPC_무장', nationId: 1 }),
                time: logicalGameNow,
            })
        );
    });

    it('NPC 군주 국가턴에서 신규 유저의 수뇌 직책과 외교 권한을 월드 상태에 반영한다', async () => {
        const buildGeneral = (overrides: Partial<TurnGeneral>): TurnGeneral => {
            const base: TurnGeneral = {
                id: 1,
                name: 'NPC군주',
                nationId: 1,
                cityId: 1,
                troopId: 0,
                stats: { leadership: 80, strength: 80, intelligence: 80 },
                turnTime: mockDate,
                role: {
                    items: { horse: null, weapon: null, book: null, item: null },
                    personality: null,
                    specialDomestic: null,
                    specialWar: null,
                },
                triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} },
                meta: { killturn: 100, belong: 2 },
                officerLevel: 12,
                experience: 0,
                dedication: 0,
                injury: 0,
                gold: 2_000,
                rice: 2_000,
                crew: 0,
                crewTypeId: 0,
                train: 0,
                atmos: 0,
                age: 30,
                npcState: 2,
            };
            return {
                ...base,
                ...overrides,
                stats: { ...base.stats, ...overrides.stats },
                meta: { ...base.meta, ...overrides.meta },
            };
        };
        const ruler = buildGeneral({});
        const user = buildGeneral({
            id: 2,
            name: '신규유저',
            npcState: 0,
            officerLevel: 1,
            meta: { killturn: 100, belong: 1 },
        });
        const city = {
            id: 1,
            name: '소성A',
            nationId: 1,
            level: 1,
            state: 0,
            population: 10_000,
            populationMax: 20_000,
            agriculture: 1_000,
            agricultureMax: 2_000,
            commerce: 1_000,
            commerceMax: 2_000,
            security: 1_000,
            securityMax: 2_000,
            supplyState: 1,
            frontState: 0,
            defence: 500,
            defenceMax: 1_000,
            wall: 500,
            wallMax: 1_000,
            meta: { trust: 98 },
        };
        const nation = {
            id: 1,
            name: 'NPC국가',
            color: '#FF0000',
            capitalCityId: 1,
            chiefGeneralId: 1,
            gold: 50_000,
            rice: 50_000,
            power: 0,
            level: 1,
            typeCode: 'che_def',
            meta: { chief_set: 0 },
        };
        const snapshot: TurnWorldSnapshot = {
            generals: [ruler, user],
            cities: [city] as any,
            nations: [nation] as any,
            troops: [],
            diplomacy: [],
            events: [],
            initialEvents: [],
            map: MINIMAL_MAP as any,
            scenarioConfig: {
                stat: { total: 300, min: 10, max: 100, npcTotal: 150, npcMax: 50, npcMin: 10, chiefMin: 70 },
                iconPath: '',
                map: {},
                const: { npcMessageFreqByDay: 144 },
                environment: { mapName: 'npc_domestic_map', unitSet: 'default' },
            },
            scenarioMeta: { startYear: 189 } as any,
            unitSet: {} as any,
        };
        const state: TurnWorldState = {
            id: 1,
            currentYear: 189,
            currentMonth: 3,
            tickSeconds: 600,
            lastTurnTime: mockDate,
            meta: { seed: 1, killturn: 100 },
        };
        const reservedTurnStore = new InMemoryReservedTurnStore(createMockPrisma() as any, {
            maxGeneralTurns: 10,
            maxNationTurns: 10,
        });
        await reservedTurnStore.loadAll();
        const wrapper = { world: null as InMemoryTurnWorld | null };
        const handler = await createReservedTurnHandler({
            reservedTurns: reservedTurnStore,
            scenarioConfig: snapshot.scenarioConfig,
            scenarioMeta: snapshot.scenarioMeta,
            map: MINIMAL_MAP as any,
            unitSet: snapshot.unitSet,
            getWorld: () => wrapper.world,
        });
        const world = new InMemoryTurnWorld(state, snapshot, {
            schedule: { entries: [{ startMinute: 0, tickMinutes: 10 }] },
            generalTurnHandler: handler,
        });
        wrapper.world = world;

        world.executeGeneralTurn(ruler);

        expect(world.getGeneralById(user.id)).toMatchObject({
            officerLevel: 11,
            meta: expect.objectContaining({ officer_city: 0, permission: 'ambassador' }),
        });
        expect(world.getNationById(nation.id)?.meta).toMatchObject({ chief_set: 1 << 11 });
    });
});
