/**
 * Copyright Super iPaaS Integration LLC, an IBM Company 2024
 */
import winston from 'winston';
import { Components, ComponentCodes } from '../src/constants/component.js';
import { Logger } from '../src/logger.js';
import { createLoggerInstance, getLogger } from '../src/utils.js';

jest.mock('../src/utils.js');

describe('Logger', () => {
	let mockCreateLoggerInstance: jest.Mock;
	let mockGetLogger: jest.Mock;
	let logger: Logger;

	beforeEach(() => {
		mockCreateLoggerInstance = jest.mocked(createLoggerInstance);
		mockCreateLoggerInstance.mockReturnValue({
			log: jest.fn(),
		} as unknown as winston.Logger);
		mockGetLogger = jest.mocked(getLogger);
		mockGetLogger.mockReturnValue({ level: jest.fn() } as unknown as winston.Logger);
		logger = new Logger(Components.ServerAppComponent);
	});

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

	it('should be created with the specified module name', () => {
		expect(logger.moduleName).toBe(Components.ServerAppComponent);
		expect(logger.componentCode).toBe(ComponentCodes[Components.ServerAppComponent]);
	});

	it('should throw an error if an invalid component code is provided', () => {
		expect(() => new Logger('InvalidComponent' as Components)).toThrow('Invalid component');
	});

	it('should format messages correctly with parameters', () => {
		const template = 'Message with param {0} and {1}';
		const result = logger.formatMessage(template, 'param1', 'param2');
		expect(result).toBe('Message with param param1 and param2');
	});

	it('should format messages correctly without parameters', () => {
		const template = 'Message without params';
		const result = logger.formatMessage(template);
		expect(result).toBe('Message without params');
	});

	it('should log info messages', () => {
		const messageKey = '0003';
		const message = 'Component1';
		const formattedMessage = `[${logger.componentCode}.${messageKey}] ${message}`;

		logger.logInfo(messageKey, message);

		expect(mockCreateLoggerInstance).toHaveBeenCalledWith(Components.ServerAppComponent.toString());
		expect(logger.logger.log).toHaveBeenCalledWith({
			level: 'info',
			message: formattedMessage,
		});
	});

	it('should log error messages', () => {
		const messageKey = '0003';
		const message = 'Component2';
		const formattedMessage = `[${logger.componentCode}.${messageKey}] ${message}`;

		logger.logError(messageKey, message);

		expect(mockCreateLoggerInstance).toHaveBeenCalledWith(Components.ServerAppComponent.toString());
		expect(logger.logger.log).toHaveBeenCalledWith({
			level: 'error',
			message: formattedMessage,
		});
	});

	it('should log debug messages', () => {
		const messageKey = '0003';
		const message = 'Component3';
		const formattedMessage = `[${logger.componentCode}.${messageKey}] ${message}`;

		logger.logDebug(messageKey, message);

		expect(mockCreateLoggerInstance).toHaveBeenCalledWith(Components.ServerAppComponent.toString());
		expect(logger.logger.log).toHaveBeenCalledWith({
			level: 'debug',
			message: formattedMessage,
		});
	});

	it('should log warn messages', () => {
		const messageKey = '0003';
		const message = 'Component4';
		const formattedMessage = `[${logger.componentCode}.${messageKey}] ${message}`;

		logger.logWarn(messageKey, message);

		expect(mockCreateLoggerInstance).toHaveBeenCalledWith(Components.ServerAppComponent.toString());
		expect(logger.logger.log).toHaveBeenCalledWith({
			level: 'warn',
			message: formattedMessage,
		});
	});

	it('should set the log level for an existing module', () => {
		const moduleName = Components.BuildComponent;
		const newLevel = 'warn';

		winston.loggers.has = jest.fn().mockReturnValue(true);

		const result = logger.setLogLevel(moduleName, newLevel);

		expect(result).toBe(0);
		expect(mockGetLogger).toHaveBeenCalledWith(moduleName);
		expect(mockGetLogger.mock.results[0].value.level).toBe(newLevel);
	});

	it('should return -1 if the module does not exist for setLogLevel', () => {
		const moduleName = 'ServerUi';
		const newLevel = 'warn';

		winston.loggers.has = jest.fn().mockReturnValue(false);

		const result = logger.setLogLevel(moduleName, newLevel);

		expect(result).toBe(-1);
		expect(mockGetLogger).not.toHaveBeenCalled();
	});
});
