import { ZipProcessor } from '../core/impl/zip-processor.impl';
import JSZip from 'jszip';

describe('ZipProcessor', () => {
    let zipProcessor: ZipProcessor;

    beforeEach(() => {
        zipProcessor = new ZipProcessor();
    });

    // Helper function to create a test ZIP file
    function createTestZip(): JSZip {
        const zip = new JSZip();

        // Add a test YAML file with multiple assets
        const yamlContent = `
kind: API
apiVersion: api.ibm.com/v1
metadata:
  name: test-api
  version: 1.0.0
  namespace: test
spec:
  value: test-value
  $ref: test:referenced-asset:1.0.0
---
kind: TestAsset
apiVersion: api.ibm.com/v1
metadata:
  name: referenced-asset
  version: 1.0.0
  namespace: test
spec:
  value: asset-value
`;

        zip.file('api.yaml', yamlContent);

        // Add another YAML file
        const anotherYamlContent = `
kind: AnotherAsset
apiVersion: api.ibm.com/v1
metadata:
  name: another-asset
  version: 1.0.0
spec:
  value: another-value
`;

        zip.file('another.yaml', anotherYamlContent);

        // Add a resource file
        zip.file('resources/test.txt', 'test resource content');

        // Add a non-YAML file
        zip.file('test.json', '{"test": "value"}');

        // Add a directory
        zip.folder('empty-dir');

        return zip;
    }

    describe('getMultiYamlFiles', () => {
        it('should extract YAML files from ZIP', async () => {
            // Arrange
            const zip = createTestZip();

            // Act
            const result = await zipProcessor.getMultiYamlFiles(zip);

            // Assert
            expect(result).toHaveLength(2); // api.yaml and another.yaml
            expect(result[0].fileName).toMatch(/api\.yaml$/);
            expect(result[1].fileName).toMatch(/another\.yaml$/);
        });

        it('should parse YAML documents correctly', async () => {
            // Arrange
            const zip = createTestZip();

            // Act
            const result = await zipProcessor.getMultiYamlFiles(zip);

            // Assert
            const apiYaml = result.find(file => file.fileName.endsWith('api.yaml'));
            expect(apiYaml).toBeDefined();
            expect(apiYaml?.assets).toHaveLength(2);
            expect(apiYaml?.assets[0].kind).toBe('API');
            expect(apiYaml?.assets[1].kind).toBe('TestAsset');
        });

        it('should handle errors gracefully', async () => {
            // Arrange
            const zip = new JSZip();
            zip.file('invalid.yaml', Buffer.from('invalid: yaml: content: - -', 'utf8'));

            // Act
            const result = await zipProcessor.getMultiYamlFiles(zip);

            // Assert
            expect(result).toHaveLength(0);
        });
    });

    describe('getAssetsFromZip', () => {
        it('should extract assets grouped by API', async () => {
            // Arrange
            const zip = createTestZip();

            // Act
            const result = await zipProcessor.getAssetsFromZip(zip);

            // Assert
            expect(Object.keys(result)).toHaveLength(1);
            expect(result['test-api']).toBeDefined();
            expect(result['test-api'].api.kind).toBe('API');
            expect(result['test-api'].relatedAssets).toHaveLength(1);
            expect(result['test-api'].relatedAssets[0].kind).toBe('TestAsset');
        });

        it('should collect related assets based on references', async () => {
            // Arrange
            const zip = createTestZip();

            // Act
            const result = await zipProcessor.getAssetsFromZip(zip);

            // Assert
            const api = result['test-api'];
            expect(api.relatedAssets[0].metadata.name).toBe('referenced-asset');
        });
    });

    describe('getResourcesFromZip', () => {
        it('should extract resources from ZIP', async () => {
            // Arrange
            const zip = createTestZip();

            // Act
            const result = await zipProcessor.getResourcesFromZip(zip);

            // Assert
            expect(Object.keys(result)).toHaveLength(1);
            expect(result['resources/test.txt']).toBe('test resource content');
        });

        it('should skip non-resource files', async () => {
            // Arrange
            const zip = createTestZip();

            // Act
            const result = await zipProcessor.getResourcesFromZip(zip);

            // Assert
            expect(result['test.json']).toBeUndefined();
        });
    });

    describe('findReferences', () => {
        it('should find references in an asset', () => {
            // Arrange
            const asset = {
                spec: {
                    $ref: 'test:reference1:1.0.0',
                    nested: {
                        $ref: 'test:reference2:1.0.0'
                    },
                    array: [
                        { $ref: 'test:reference3:1.0.0' },
                        { value: 'not a reference' }
                    ]
                }
            };

            // Act
            const result = (zipProcessor as any).findReferences(asset);

            // Assert
            expect(result).toHaveLength(3);
            expect(result).toContain('test:reference1:1.0.0');
            expect(result).toContain('test:reference2:1.0.0');
            expect(result).toContain('test:reference3:1.0.0');
        });
    });

    describe('findAssetByRef', () => {
        it('should find an asset by reference', () => {
            // Arrange
            const assets = [
                {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'asset1',
                        namespace: 'test',
                        version: '1.0.0'
                    }
                },
                {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'asset2',
                        namespace: 'test',
                        version: '2.0.0'
                    }
                }
            ];

            // Act
            const result = (zipProcessor as any).findAssetByRef('test:asset1:1.0.0', assets);

            // Assert
            expect(result).toBe(assets[0]);
        });

        it('should find asset by name only', () => {
            // Arrange
            const assets = [
                {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'asset1',
                        namespace: 'test',
                        version: '1.0.0'
                    }
                }
            ];

            // Act - Just use the name without namespace
            const result = (zipProcessor as any).findAssetByRef('test:asset1', assets);

            // Assert
            expect(result).toBeDefined();
            if (result) {
                expect(result.metadata.name).toBe('asset1');
                expect(result.metadata.namespace).toBe('test');
            }
        });

        it('should handle missing version in reference', () => {
            // Arrange
            const assets = [
                {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'asset1',
                        namespace: 'test',
                        version: '1.0.0'
                    }
                }
            ];

            // Act
            const result = (zipProcessor as any).findAssetByRef('test:asset1', assets);

            // Assert
            expect(result).toBeDefined();
            if (result) {
                expect(result.metadata.name).toBe('asset1');
                expect(result.metadata.namespace).toBe('test');
            }
        });

        it('should handle invalid reference format', () => {
            // Arrange
            const assets = [
                {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'asset1',
                        namespace: 'test',
                        version: '1.0.0'
                    }
                }
            ];

            // Act
            const result = (zipProcessor as any).findAssetByRef('invalid-ref', assets);

            // Assert
            expect(result).toBeUndefined();
        });
    });
});

