import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { encodeMap, parseMap } from '@rpg/engine';
import {
    buildMeadowsMap,
    buildPondsMap,
    buildZvenetsMap,
    buildRustGroveMap,
    buildOldSidingMap,
    buildMachineHallMap,
    buildHouseElderMap,
    buildShopMap
} from '../../src/data/map';

/**
 * Карты-файлы: fresh-тест (диск == генератор) и round-trip RLE — read-only
 * и идут в каждом `npm test`. Запись файлов — только при RUN_MAPS_GEN=1
 * (`npm run maps`): обычный прогон тестов не трогает рабочее дерево.
 */

const here = dirname(fileURLToPath(import.meta.url));
const outDir = resolve(here, '../../assets/maps');
const gen = !!process.env.RUN_MAPS_GEN;
const d = gen ? describe : describe.skip;

const MAPS: Record<string, ReturnType<typeof buildMeadowsMap>> = {
    meadows: buildMeadowsMap(),
    ponds: buildPondsMap(),
    zvenets: buildZvenetsMap(),
    rust_grove: buildRustGroveMap(),
    old_siding: buildOldSidingMap(),
    machine_hall: buildMachineHallMap(),
    house_elder: buildHouseElderMap(),
    shop: buildShopMap()
};

describe('карты-файлы на диске равны генераторам (fresh)', () => {
    for (const [id, data] of Object.entries(MAPS)) {
        it(`${id}.map не разъехался с генератором (npm run maps лечит)`, () => {
            const path = resolve(outDir, `${id}.map`);
            const onDisk = JSON.parse(readFileSync(path, 'utf8'));
            expect(onDisk).toEqual(encodeMap(data));
        });
    }
});

describe('rpg-map, RLE (round-trip, read-only)', () => {
    for (const [id, data] of Object.entries(MAPS)) {
        it(`${id}: encode -> parse восстанавливает карту`, () => {
            const back = parseMap(JSON.parse(JSON.stringify(encodeMap(data))));

            expect(back.width).toBe(data.width);
            expect(back.height).toBe(data.height);
            expect(back.blocked).toEqual(data.blocked);
            expect(back.tiles).toEqual(data.tiles);
            expect(back.tall).toEqual(data.tall);
        });
    }

    it('RLE реально сжимает карты', () => {
        for (const [id, data] of Object.entries(MAPS)) {
            const file = encodeMap(data);
            const pairs = file.tiles as [number, number][];
            // Серий меньше, чем тайлов: RLE что-то ужал, несмотря на пятнистость
            expect(pairs.length).toBeLessThan(data.tiles.length);
            expect(file.encoding).toBe('rle');
            expect(id.length).toBeGreaterThan(0);
        }
    });
});

d('запись карт-файлов (RUN_MAPS_GEN=1 / npm run maps)', () => {
    for (const [id, data] of Object.entries(MAPS)) {
        it(`${id}: пишет assets/maps/${id}.map`, () => {
            const file = encodeMap(data);
            mkdirSync(outDir, { recursive: true });
            writeFileSync(resolve(outDir, `${id}.map`), JSON.stringify(file));
            expect(true).toBe(true);
        });
    }
});