import { Encoder } from '../dist/encoder.js';
import { decode, jsDecode } from '../dist/helper/string-decoder.js';

/**
 * 测试编码器和解码器
 */
function testEncoding(encoder: Pick<TextEncoder, 'encode'>, decoder: Pick<TextDecoder, 'decode'>) {
    expect(decoder.decode(encoder.encode(''))).toEqual('');
    expect(decoder.decode(encoder.encode('a'))).toEqual('a');
    expect(decoder.decode(encoder.encode('p4'))).toEqual('p4');
    expect(decoder.decode(encoder.encode('t0'))).toEqual('t0');
    expect(decoder.decode(encoder.encode('qwe'))).toEqual('qwe');
    expect(decoder.decode(encoder.encode('1qaz'))).toEqual('1qaz');
    expect(decoder.decode(encoder.encode('123456'))).toEqual('123456');
    expect(decoder.decode(encoder.encode('123465'))).toEqual('123465');
    expect(decoder.decode(encoder.encode('1234651'))).toEqual('1234651');
    expect(decoder.decode(encoder.encode('1234651'))).toEqual('1234651');
    expect(decoder.decode(encoder.encode('12346511234651'))).toEqual('12346511234651');
    expect(decoder.decode(encoder.encode('123465112346511234651'))).toEqual('123465112346511234651');
    expect(decoder.decode(encoder.encode('abc 你好 😊🤣'))).toEqual('abc 你好 😊🤣');
    expect(decoder.decode(encoder.encode('abc 你好abc 你好abc 你好 😊🤣'))).toEqual('abc 你好abc 你好abc 你好 😊🤣');
    expect(decoder.decode(encoder.encode('水'.repeat(127)))).toEqual('水'.repeat(127));
    expect(decoder.decode(encoder.encode('水'.repeat(32767)))).toEqual('水'.repeat(32767));
    expect(decoder.decode(encoder.encode('abc 你好 😊🤣'.repeat(1000)))).toEqual('abc 你好 😊🤣'.repeat(1000));

    for (let length = 1; length < 1024; length++) {
        const expected = 'a'.repeat(length);
        const actual = decoder.decode(encoder.encode(expected));
        expect(actual).toEqual(expected);

        const expected2 = '©'.repeat(length);
        const actual2 = decoder.decode(encoder.encode(expected2));
        expect(actual2).toEqual(expected2);

        const expected3 = '水'.repeat(length);
        const actual3 = decoder.decode(encoder.encode(expected3));
        expect(actual3).toEqual(expected3);

        const expected4 = '😊'.repeat(length);
        const actual4 = decoder.decode(encoder.encode(expected4));
        expect(actual4).toEqual(expected4);

        const bad = new Uint8Array(length);
        bad.fill(0xff);
        expect(decoder.decode(bad)).toEqual('\uFFFD'.repeat(length));

        // eslint-disable-next-line unicorn/prefer-code-point
        bad.fill('a'.charCodeAt(0), 0, length - 1);
        expect(decoder.decode(bad)).toEqual('a'.repeat(length - 1) + '\uFFFD');
    }

    {
        // 检查所有单字节
        for (let index = 0; index < 0xffff; index++) {
            // 跳过 Surrogate
            if (index >= 0xd800 && index <= 0xdfff) continue;
            // eslint-disable-next-line unicorn/prefer-code-point
            const expected = String.fromCharCode(index);
            const encoded = encoder.encode(expected);
            const actual = decoder.decode(encoded);
            if (expected !== actual) {
                expect(actual).toEqual(expected);
            }
        }
    }

    {
        // 检查所有代理项
        for (let index = 0x1_0000; index < 0x10_ffff; index++) {
            const expected = String.fromCodePoint(index);
            const actual = decoder.decode(encoder.encode(expected));
            if (expected !== actual) expect(actual).toEqual(expected);
        }
    }

    {
        // 检查所有 2 字节 ASCII
        for (let index = 0; index < 0x80; index++) {
            for (let index2 = 0; index2 < 0x80; index2++) {
                // eslint-disable-next-line unicorn/prefer-code-point
                const expected = String.fromCharCode(index, index2);
                const actual = decoder.decode(encoder.encode(expected));
                if (expected !== actual) expect(actual).toEqual(expected);
            }
        }
    }
}

test('encode string', () => {
    testEncoding(
        {
            encode(s) {
                const encoder = new Encoder();
                const buffer = encoder.encode(s);
                if (buffer[0] === 67) return buffer.subarray(1);
                if (buffer[1] === 105) return buffer.subarray(3);
                if (buffer[1] === 73) return buffer.subarray(4);
                if (buffer[1] === 108) return buffer.subarray(6);
                throw new Error('Invalid buffer');
            },
        },
        new TextDecoder('utf-8', { ignoreBOM: true, fatal: false }),
    );
});

test('decode string', () => {
    testEncoding(new TextEncoder(), {
        decode(buffer) {
            if (!(buffer instanceof Uint8Array)) return '';
            return decode(buffer, 0, buffer.byteLength);
        },
    });
});

test('decode string js', () => {
    testEncoding(new TextEncoder(), {
        decode(buffer) {
            if (!(buffer instanceof Uint8Array)) return '';
            return jsDecode(buffer, 0, buffer.byteLength);
        },
    });
});

test('decode malformed', () => {
    // eslint-disable-next-line unicorn/prefer-code-point
    expect(decode(new Uint8Array([0xff, 'a'.charCodeAt(0)]), 0, 2)).toEqual('\uFFFDa');
    // eslint-disable-next-line unicorn/prefer-code-point
    expect(jsDecode(new Uint8Array([0xff, 'a'.charCodeAt(0)]), 0, 2)).toEqual('\uFFFDa');
});
