import { describe, expect, it } from 'vitest';
import { VoxelGrid } from '../../voxel/grid';
import { assertModelValid, decodeModel, encodeModel, validateModel } from '../format';

/** Маленькая сетка-фабрика: 2×3×2 с одним цветным вокселем. */
function makeGrid(): VoxelGrid {
    const g = new VoxelGrid(2, 3, 2);
    g.set(1, 2, 1, 3);
    g.set(0, 0, 0, 1);
    return g;
}

const PALETTE = { 1: '#4a5340', 3: '#626c4c' };

describe('формат модели (encode/decode/validate)', () => {
    it('roundtrip: encode→decode даёт ту же сетку', () => {
        const g = makeGrid();
        const back = decodeModel(encodeModel(g, PALETTE));
        expect(back.sx).toBe(2);
        expect(back.sy).toBe(3);
        expect(back.sz).toBe(2);
        for (let z = 0; z < 2; z++)
            for (let y = 0; y < 3; y++)
                for (let x = 0; x < 2; x++) expect(back.get(x, y, z)).toBe(g.get(x, y, z));
    });

    it('encode не мутирует исходную сетку', () => {
        const g = makeGrid();
        const before = g.count;
        encodeModel(g, PALETTE);
        expect(g.count).toBe(before);
    });

    it('validate: валидная модель — пустой список проблем', () => {
        const m = encodeModel(makeGrid(), PALETTE);
        expect(validateModel(m)).toEqual([]);
    });

    it('validate: не-объект и плохой размер', () => {
        expect(validateModel(null).length).toBeGreaterThan(0);
        expect(validateModel('x').length).toBeGreaterThan(0);
        const m = encodeModel(makeGrid(), PALETTE);
        expect(validateModel({ ...m, size: [0, 3, 2] }).some((p) => p.includes('size'))).toBe(true);
        expect(validateModel({ ...m, size: [2, 3] }).some((p) => p.includes('size'))).toBe(true);
    });

    it('validate: битый base64 и неверная длина данных', () => {
        const m = encodeModel(makeGrid(), PALETTE);
        expect(validateModel({ ...m, data: 'не base64!!!' }).some((p) => p.includes('base64'))).toBe(true);
        expect(validateModel({ ...m, size: [2, 3, 3] }).some((p) => p.includes('байт'))).toBe(true);
    });

    it('validate: слоты вне 1..255, плохой hex, воксель без слота', () => {
        const m = encodeModel(makeGrid(), PALETTE);
        expect(validateModel({ ...m, palette: { 0: '#ffffff' } }).some((p) => p.includes('1..255'))).toBe(true);
        expect(validateModel({ ...m, palette: { 1: 'красный' } }).some((p) => p.includes('hex'))).toBe(true);
        // воксель слота 9, которого нет в палитре
        const bad = { ...m, data: btoa(String.fromCharCode(9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) };
        expect(validateModel(bad).some((p) => p.includes('отсутствует в палитре'))).toBe(true);
    });

    it('assertModelValid кидает с текстом первой проблемы', () => {
        const m = encodeModel(makeGrid(), PALETTE);
        expect(() => assertModelValid({ ...m, size: [1, 1, 9999] })).toThrow(/size/);
        expect(() => assertModelValid(m)).not.toThrow();
    });

    it('decodeModel бросает на невалидной модели', () => {
        expect(() => decodeModel({ size: [2, 2, 2], palette: { 1: '#111111' }, data: 'AAAA' })).toThrow();
    });
});