import { Object3D } from 'three';
import { DIVEGLTFIO } from '../GLTFIO';

import { type GLTF } from 'three/examples/jsm/loaders/GLTFLoader';

let testGLTFIO: DIVEGLTFIO;

describe('dive/io/gltf/DIVEGLTFIO', () => {
    beforeEach(() => {
        testGLTFIO = new DIVEGLTFIO();
    });

    afterEach(() => {
        jest.clearAllMocks();
    });

    it('should instantiate', () => {
        expect(testGLTFIO).toBeDefined();
    });

    it('should import from URL', async () => {
        const mockGLTF = {} as GLTF;

        // test without progress callback
        jest.spyOn(testGLTFIO['_importer'], 'loadAsync').mockImplementationOnce(
            (
                uri: string,
                progEvent?: (p: ProgressEvent<EventTarget>) => void,
            ) =>
                new Promise<GLTF>((resolve) => {
                    if (progEvent)
                        progEvent({
                            loaded: 0,
                            total: 1,
                        } as ProgressEvent<EventTarget>);

                    resolve(mockGLTF);
                }),
        );

        const resultWithoutProgress = await testGLTFIO.Import(
            'test url',
            undefined,
        );

        expect(resultWithoutProgress).toStrictEqual(mockGLTF);

        // test with progress callback
        jest.spyOn(testGLTFIO['_importer'], 'loadAsync').mockImplementationOnce(
            (
                uri: string,
                progEvent?: (p: ProgressEvent<EventTarget>) => void,
            ) =>
                new Promise<GLTF>((resolve) => {
                    if (progEvent)
                        progEvent({
                            loaded: 0,
                            total: 1,
                        } as ProgressEvent<EventTarget>);

                    resolve(mockGLTF);
                }),
        );

        const onProgress = jest.fn();

        const resultWithProgress = await testGLTFIO.Import(
            'test url',
            onProgress,
        );

        expect(resultWithProgress).toStrictEqual(mockGLTF);
        expect(onProgress).toHaveBeenCalledTimes(1);
    });

    it('should export to URL', async () => {
        const mockObject = new Object3D();

        // export as JSON object
        jest.spyOn(testGLTFIO['_exporter'], 'parseAsync').mockResolvedValueOnce(
            {},
        );

        const json = await testGLTFIO.Export(mockObject, false, false);
        expect(json).toBeDefined();

        // export as array buffer
        jest.spyOn(testGLTFIO['_exporter'], 'parseAsync').mockResolvedValueOnce(
            new ArrayBuffer(0),
        );

        const binary = await testGLTFIO.Export(mockObject, true, false);
        expect(binary).toBeDefined();

        // export as array buffer (only visible)

        jest.spyOn(testGLTFIO['_exporter'], 'parseAsync').mockResolvedValueOnce(
            new ArrayBuffer(0),
        );

        const binaryOnlyVisible = await testGLTFIO.Export(
            mockObject,
            true,
            true,
        );
        expect(binaryOnlyVisible).toBeDefined();
    });
});
