import fs from 'fs-extra';
import { showError, showInfo } from '../../helpers/common/message-helper.js';
import { setGatewayEndpoint, getGatewayEndpoints, GatewayEndpointJson } from './config.js'; // Adjust the import path
import { GatewayResponseAPI } from '../../model/studio/deploy-response-model.js';
import { constructKey } from '../../helpers/apim/test-helper.js';
import { ALL_ENDPOINTS_FETCHED_FROM_STORE,
	CONFIG,
	DEPLOYMENT_DATA_UPDATED,
	NO_DATA_TO_PROCESS,
	NO_ENDPOINTS_IN_STORE,
	NO_ENDPOINTS_STORE_CONFIGURED,
	NO_UPDATES_NEEDED,
	SOME_ENDPOINTS_MISSING_FROM_STORE } from '../../constants/message-constants.js';


jest.mock('path', () => ({
	join: jest.fn().mockImplementation((...args) => args.join('/')),
	resolve: jest.fn().mockImplementation((...args) => args.join('/')),
}));

jest.mock('fs-extra', () => ({
	ensureDir: jest.fn(),
	pathExists: jest.fn(),
	writeJson: jest.fn(),
	readJson: jest.fn(),
}));

jest.mock('../../helpers/common/message-helper.js', () => ({
	showInfo: jest.fn(),
	showError: jest.fn(),
	showWarning: jest.fn(),
}));
jest.mock('../../helpers/apim/test-helper.js', () => ({
	constructKey: jest.fn(),
}));

jest.mock('env-paths', () => {
	return jest.fn((name: string) => ({
		data: `/mock/path/${name}-data`,
		config: `/mock/path/${name}-config`,
		cache: `/mock/path/${name}-cache`,
		log: `/mock/path/${name}-log`,
		temp: `/mock/path/${name}-temp`,
	}));
});

jest.mock('../../debug/debug-manager.js', () => ({
	DebugManager: {
		getInstance: jest.fn().mockReturnValue({
			setDebugEnabled: jest.fn(),
			isDebugEnabled: jest.fn().mockReturnValue(true),
		}),
	},
}));


describe('Configuration Test Suite', () => {
	beforeEach(() => {
		(constructKey as jest.Mock).mockImplementation((apiResponse) => apiResponse.name);
		jest.clearAllMocks();
	});
	describe('setGatewayEndpoint', () => {
		const mockResponse: GatewayResponseAPI[] = [
			{
				name: 'api1',
				gatewayEndpoints: ['https://api1.com'],
				kind: 'API',
				namespace: 'default',
				version: '1.0.0',
				assetName: 'asset1',
			},
			{
				name: 'api2',
				gatewayEndpoints: ['https://api2.com'],
				kind: 'API',
				namespace: 'default',
				version: '1.1.0',
				assetName: 'asset2',
			}
		];
    
		beforeEach(() => {
			jest.clearAllMocks();
		});
    
		it('should show NO_DATA_TO_PROCESS when response is empty', async () => {
			const emptyResponse: GatewayResponseAPI[] = [];
			await setGatewayEndpoint(emptyResponse);
    
			expect(showError).toHaveBeenCalledWith(CONFIG + NO_DATA_TO_PROCESS);
		});
    
		it('should create config if file does not exist and set new endpoints', async () => {
			(fs.pathExists as jest.Mock).mockResolvedValueOnce(false);
			(fs.pathExists as jest.Mock).mockResolvedValueOnce(true);
			(fs.readJson as jest.Mock).mockResolvedValue({ deployments: [] });
			(fs.writeJson as jest.Mock).mockResolvedValueOnce(undefined);
    
			await setGatewayEndpoint(mockResponse);
    
			expect(fs.writeJson).toHaveBeenCalled();
			expect(showInfo).toHaveBeenCalledWith(expect.any(String));
		});
    
		it('should update the config file if the endpoints need to be updated', async () => {
			const currentData = {
				deployments: [
					{
						api: 'api1',
						endpoints: ['https://old-api1.com'],
					}
				]
			};
    
			(fs.pathExists as jest.Mock).mockResolvedValue(true);
			(fs.readJson as jest.Mock).mockResolvedValue(currentData);
			(fs.writeJson as jest.Mock).mockResolvedValue(undefined);
    
			await setGatewayEndpoint(mockResponse);
    
			expect(fs.writeJson).toHaveBeenCalled();
			expect(showInfo).toHaveBeenCalledWith(CONFIG + DEPLOYMENT_DATA_UPDATED);
		});
    
		it('should show NO_UPDATES_NEEDED if no changes are required', async () => {
			const currentData = {
				deployments: [
					{
						api: 'api1',
						endpoints: ['https://api1.com'],
					},
					{
						api: 'api2',
						endpoints: ['https://api2.com'],
					}
				]
			};
    
			(fs.pathExists as jest.Mock).mockResolvedValue(true);
			(fs.readJson as jest.Mock).mockResolvedValue(currentData);
    
			await setGatewayEndpoint(mockResponse);
    
			expect(showInfo).toHaveBeenCalledWith(CONFIG + NO_UPDATES_NEEDED);
			expect(fs.writeJson).not.toHaveBeenCalled();
		});
    
		it('should append new deployments to the config file if they are missing', async () => {
			const currentData = {
				deployments: [
					{
						api: 'api1',
						endpoints: ['https://api1.com'],
					},
				],
			};
    
			(fs.pathExists as jest.Mock).mockResolvedValue(true);
			(fs.readJson as jest.Mock).mockResolvedValue(currentData);
			(fs.writeJson as jest.Mock).mockResolvedValue(undefined);
    
			await setGatewayEndpoint(mockResponse);
    
			expect(fs.writeJson).toHaveBeenCalledWith(expect.any(String), {
				deployments: expect.arrayContaining([
					expect.objectContaining({ api: 'api1', endpoints: ['https://api1.com'] }),
					expect.objectContaining({ api: 'api2', endpoints: ['https://api2.com'] }),
				]),
			}, { spaces: 2 });
			expect(showInfo).toHaveBeenCalledWith(CONFIG + DEPLOYMENT_DATA_UPDATED);
		});
    
		it('should handle errors when setting the endpoints fails', async () => {
			(fs.pathExists as jest.Mock).mockRejectedValue(new Error('File system error'));
			await setGatewayEndpoint(mockResponse);
    
			expect(showError).toHaveBeenCalledWith(expect.any(String));
		});
	});

	describe('getGatewayEndpoints', () => {
		it('should return found and not found endpoints', async () => {
			(fs.pathExists as jest.Mock).mockResolvedValue(true);
			(fs.readJson as jest.Mock).mockResolvedValue({
				deployments: [
					{ api: 'api1', endpoints: ['http://example.com'] },
					{ api: 'api2', endpoints: ['http://example2.com'] },
				],
			});

			const result = await getGatewayEndpoints('api1,api3');

			expect(result).toEqual({
				foundEndpoints: {
					api1: ['http://example.com'],
				},
				notFoundApis: 'api3',
			});
			expect(showInfo).toHaveBeenCalledWith(CONFIG + SOME_ENDPOINTS_MISSING_FROM_STORE);
		});

		it('should return found endpoints and empty notFoundApis if all requested APIs are found', async () => {
			const mockData = {
				deployments: [
					{ api: 'api1', endpoints: ['http://example.com'] },
					{ api: 'api2', endpoints: ['http://example2.com'] },
				],
			} as  GatewayEndpointJson;
        
			(fs.pathExists as jest.Mock).mockResolvedValue(true);
			(fs.readJson as jest.Mock).mockResolvedValue(mockData);
        
			const result = await getGatewayEndpoints('api1,api2');
        
			expect(result).toEqual({ foundEndpoints: { api1: ['http://example.com'], api2: ['http://example2.com'] }, notFoundApis: '' });
			expect(showInfo).toHaveBeenCalledWith(CONFIG + ALL_ENDPOINTS_FETCHED_FROM_STORE);
		});
        
		it('should return some found endpoints and missing APIs', async () => {
			const mockData: GatewayEndpointJson = {
				deployments: [
					{ api: 'api1', endpoints: ['http://example.com'] },
				],
			};
        
			(fs.pathExists as jest.Mock).mockResolvedValue(true);
			(fs.readJson as jest.Mock).mockResolvedValue(mockData);
        
			const result = await getGatewayEndpoints('api1,api2');
        
			expect(result).toEqual({ foundEndpoints: { api1: ['http://example.com'] }, notFoundApis: 'api2' });
			expect(showInfo).toHaveBeenCalledWith(CONFIG + SOME_ENDPOINTS_MISSING_FROM_STORE);
		});
        
		it('should return empty found endpoints and all APIs as missing', async () => {
			const mockData: GatewayEndpointJson = {
				deployments: [],
			};
        
			(fs.pathExists as jest.Mock).mockResolvedValue(true);
			(fs.readJson as jest.Mock).mockResolvedValue(mockData);
        
			const result = await getGatewayEndpoints('api1,api2');
        
			expect(result).toEqual({ foundEndpoints: {}, notFoundApis: 'api1,api2' });
			expect(showInfo).toHaveBeenCalledWith(CONFIG + NO_ENDPOINTS_IN_STORE);
		});
        
		it('should handle case where file does not exist', async () => {
			(fs.pathExists as jest.Mock).mockResolvedValue(false);
			const result = await getGatewayEndpoints('api1');
			expect(result).not.toBeNull();
			expect(showInfo).toHaveBeenCalledWith(CONFIG + NO_ENDPOINTS_STORE_CONFIGURED);
		});
        
	});
});