import { Buffer } from 'node:buffer';
import { encode } from '../dist/index.js';
import { toArray, resetEnv } from './.utils.ts';

const STR_BYTE_LENGTH = 128 * 1024 * 1024 - 20;

/**
 * 构造测试字符串
 */
function makeString(char: string, byteLength: number): [Uint8Array<ArrayBuffer>, string] {
    const encoded = new TextEncoder().encode(char);
    return [encoded, char.repeat(byteLength / encoded.byteLength)];
}

const [str1Data, str1] = makeString('a', STR_BYTE_LENGTH);
// \u04d2 = Ӓ, 2 bytes in utf-8
const [str2Data, str2] = makeString('Ӓ', STR_BYTE_LENGTH);
// \u6c34 = 水, 3 bytes in utf-8
const [str3Data, str3] = makeString('水', STR_BYTE_LENGTH);
// \u1f600 = 😀, 4 bytes in utf-8 (0xF0 0x9F 0x98 0x80)
const [str4Data, str4] = makeString('😀', STR_BYTE_LENGTH);
// bad surrogate pair
const [strXData, strX] = makeString('😀'[0], STR_BYTE_LENGTH);

const strErr = 'a'.repeat(128 * 1024 * 1024 + 1);

const ENCODED_PREFIX_LENGTH = 6;

describe(`encode huge string`, () => {
    describe(`with node buffer`, () => {
        it(`~128M [1]`, () => {
            const encoded = encode(str1);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str1Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str1Data, ...str1Data),
            );
        });
        it(`~128M [2]`, () => {
            const encoded = encode(str2);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str2Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str2Data, ...str2Data),
            );
        });
        it(`~128M [3]`, () => {
            const encoded = encode(str3);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str3Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str3Data, ...str3Data),
            );
        });
        it(`~128M [4]`, () => {
            const encoded = encode(str4);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str4Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str4Data, ...str4Data),
            );
        });
        it(`~128M [bad surrogate pair]`, () => {
            const encoded = encode(strX);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + strXData.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...strXData, ...strXData),
            );
        });
        it(`128M [error]`, () => {
            expect(() => encode(strErr)).toThrow(/Buffer has exceed max size/);
        });
    });
    describe(`without node buffer`, () => {
        beforeAll(() => {
            // @ts-expect-error remove buffer
            globalThis.Buffer = undefined;
            resetEnv();
        });

        afterAll(() => {
            globalThis.Buffer = Buffer;
            resetEnv();
        });

        it(`~128M [1]`, () => {
            const encoded = encode(str1);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str1Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str1Data, ...str1Data),
            );
        });
        it(`~128M [2]`, () => {
            const encoded = encode(str2);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str2Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str2Data, ...str2Data),
            );
        });
        it(`~128M [3]`, () => {
            const encoded = encode(str3);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str3Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str3Data, ...str3Data),
            );
        });
        it(`~128M [4]`, () => {
            const encoded = encode(str4);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + str4Data.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...str4Data, ...str4Data),
            );
        });
        it(`~128M [bad surrogate pair]`, () => {
            const encoded = encode(strX);
            expect(encoded.length).toBe(STR_BYTE_LENGTH + ENCODED_PREFIX_LENGTH);
            expect(toArray(encoded.slice(0, ENCODED_PREFIX_LENGTH + strXData.length * 2))).toEqual(
                toArray('S', 'l', 0x7, 0xff, 0xff, 0xec, ...strXData, ...strXData),
            );
        });
        it(`128M [error]`, () => {
            expect(() => encode(strErr)).toThrow(/Buffer has exceed max size/);
        });
    });
});
