/**
 * Copyright IBM Corp. 2024, 2025
 */

/* eslint-disable no-unused-vars */
import {
  createAssetReferenceMap,
  convertNumberToString,
  isValidAsset,
  addErrorToResponse,
  constructErrorResponse,
  extractGatewayTypes,
  createPathReferenceMap,
  updatePathRefMap,
  validateMinAssets,
  updateRefs,
  processRef,
  checkFileExtension,
  isRelativePath,
} from '../src/index.js';

import yaml from 'js-yaml';
import path from 'path';
import fs from 'fs';

jest.mock('@apic/studio-shared', () => ({
  Logger: {
    debug: jest.fn(),
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    log: jest.fn(),
    createChildLogger: jest.fn().mockReturnThis(),
  },
  LogComponent: () => (target: any) => target,
  Component: {
    Build: 'Build',
    All: 'Studio',
  },
  ErrorResponse: jest.fn(),
  Metadata_Ref: jest.fn(),
  SpecObject: jest.fn(),
  YamlContent: jest.fn(),
  UpperCaseKinds: jest.fn(),
  loadYaml: jest.fn((content) => require('js-yaml').load(content)),

  SchemaHandler: jest.fn().mockImplementation(() => ({
    getSchema: jest.fn().mockReturnValue(JSON.stringify({ type: 'object' })),
  })),
}));

jest.mock('@apic/studio-client-model', () => ({
  AssetModelKindConstants: {
    API: 'API',
  },
  GatewayLabels: {
    WMGW: 'webMethods',
    LWGW: 'nano',
    DPGW: 'datapower',
  },
}));

// Mock JSZip for specific tests
const mockJSZip = {
  loadAsync: jest.fn(),
  file: jest.fn(),
  files: {},
  forEach: jest.fn(),
  generateAsync: jest.fn(),
};

jest.mock('jszip', () => {
  return jest.fn().mockImplementation(() => mockJSZip);
});

import {
  createAssetReferenceMap,
  convertNumberToString,
  isValidAsset,
  addErrorToResponse,
  constructErrorResponse,
  extractGatewayTypes,
  createPathReferenceMap,
  updatePathRefMap,
  validateMinAssets,
  updateRefs,
  processRef,
  checkFileExtension,
  isRelativePath,
} from '../src/index.js';
import { YamlContent } from '@apic/studio-shared';

describe('validateYamlFiles', () => {
  it('should create asset reference map successfully', async () => {
    const zipFilePath = path.resolve(__dirname, './assets/gateway-asset.zip');
    const Buffer = fs.readFileSync(zipFilePath);
    const result = await createAssetReferenceMap(Buffer);
    expect(result).not.toBe(undefined);
    expect(result).not.toBe(false);
  });

  it('should log error for invalid YAML parsing', async () => {
    const zipFilePath = path.resolve(__dirname, './assets/invalid-yaml-asset.zip');
    const zipBuffer = fs.readFileSync(zipFilePath);
    await createAssetReferenceMap(zipBuffer);
  });

  it('should convert number to string correctly', () => {
    expect(convertNumberToString(1)).toBe('1.0');
    expect(convertNumberToString(1.5)).toBe('1.5');
    expect(convertNumberToString('1.5')).toBe('1.5');
  });

  it('should return true for valid YAML content', () => {
    const filePath = path.resolve(__dirname, './assets/validate/api.yaml');
    const buffer = fs.readFileSync(filePath);
    const yamlContent = yaml.load(buffer.toString()) as YamlContent;
    expect(isValidAsset(yamlContent)).toBe(true);
  });
});

describe('checking error response', () => {
  it('should construct an error response object', () => {
    addErrorToResponse('ERR001', 'field1', 'Error description 1');
    addErrorToResponse('ERR002', 'field2', 'Error description 2');
    const response = constructErrorResponse();
    expect(response).not.toEqual([]);
  });
});

describe('extractGatewayTypes', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should extract gateway types from gateways.json', async () => {
    // Setup mock for JSZip
    const mockFileContent = JSON.stringify({
      gateways: [
        {
          gatewayURL: 'http://example.com',
          gatewayUser: 'user',
          gatewaySecret: 'secret',
          gatewayTypes: ['webMethods', 'nano'],
        },
      ],
    });

    mockJSZip.loadAsync.mockResolvedValue({
      file: jest.fn().mockReturnValue({
        async: jest.fn().mockResolvedValue(mockFileContent),
      }),
    });

    const result = await extractGatewayTypes(Buffer.from('test'));
    expect(result).toEqual(['webMethods', 'nano']);
    expect(mockJSZip.loadAsync).toHaveBeenCalled();
  });

  it('should return empty array when gateways.json is not found', async () => {
    mockJSZip.loadAsync.mockResolvedValue({
      file: jest.fn().mockReturnValue(null),
    });

    const result = await extractGatewayTypes(Buffer.from('test'));
    expect(result).toEqual([]);
  });

  it('should handle errors and return empty array', async () => {
    mockJSZip.loadAsync.mockRejectedValue(new Error('Test error'));

    const result = await extractGatewayTypes(Buffer.from('test'));
    expect(result).toEqual([]);
  });
});

describe('file utility functions', () => {
  it('should check file extension correctly', () => {
    expect(checkFileExtension('file.yml')).toBe(true);
    expect(checkFileExtension('file.yaml')).toBe(true);
    expect(checkFileExtension('file.txt')).toBe(false);
  });

  it('should detect relative paths correctly', () => {
    expect(isRelativePath('./file.yml')).toBe(true);
    expect(isRelativePath('../file.yml')).toBe(true);
    expect(isRelativePath('file.yml')).toBe(false);
    expect(isRelativePath('/absolute/path/file.yml')).toBe(false);
  });
});

describe('path reference functions', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should create path reference map', async () => {
    // Setup mock for JSZip
    mockJSZip.loadAsync.mockResolvedValue({
      files: {
        'file.yaml': {
          dir: false,
          async: jest.fn().mockResolvedValue('spec:\n  $path: path/to/file.txt'),
        },
      },
    });

    const result = await createPathReferenceMap(Buffer.from('test'));
    expect(result).toBeInstanceOf(Map);
  });

  it('should update path reference map', async () => {
    const refMap = new Map<string, boolean>();
    refMap.set('file.txt', false);

    // Mock JSZip.loadAsync directly
    const mockZipInstance = {
      forEach: jest.fn().mockImplementation((callback: (path: string, obj: any) => void) => {
        callback('resources/file.txt', { name: 'file.txt' });
      }),
    };

    // Replace the original implementation for this test
    const originalJSZip = require('jszip');
    originalJSZip.loadAsync = jest.fn().mockResolvedValue(mockZipInstance);

    await updatePathRefMap(Buffer.from('test'), refMap);
    expect(refMap.get('file.txt')).toBe(true);
  });
});

describe('validateMinAssets', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should return true when yaml files exist', async () => {
    mockJSZip.loadAsync.mockResolvedValue({
      files: {
        'file.yaml': { dir: false },
      },
    });

    const result = await validateMinAssets(Buffer.from('test'));
    expect(result).toBe(true);
  });

  it('should return false when no yaml files exist', async () => {
    mockJSZip.loadAsync.mockResolvedValue({
      files: {
        'file.txt': { dir: false },
      },
    });

    const result = await validateMinAssets(Buffer.from('test'));
    expect(result).toBe(false);
  });

  it('should handle errors and return false', async () => {
    mockJSZip.loadAsync.mockRejectedValue(new Error('Test error'));

    const result = await validateMinAssets(Buffer.from('test'));
    expect(result).toBe(false);
  });
});

describe('reference processing functions', () => {
  it('should process ref correctly', () => {
    expect(processRef('namespace:name:1')).toBe('namespace:name:1.0');
    expect(processRef('namespace:name:1.5')).toBe('namespace:name:1.5');
    expect(processRef('namespace:name:string')).toBe('namespace:name:string');
  });

  it('should update refs in yaml content', () => {
    const yamlContent = {
      spec: {
        $ref: 'namespace:name:1',
      },
    };
    const versionMap = new Map<string, boolean>();
    versionMap.set('namespace:name:1', false);

    const result = updateRefs(yamlContent as any, versionMap);
    expect(result.spec.$ref).toBe('namespace:name:1.0');
  });
});
