/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Copyright Super iPaaS Integration LLC, an IBM Company 2024
*/
import fs from 'fs-extra';
import path from 'path';
import {showError, showInfo} from './message-helper.js';
import {isNullOrUndefined} from './data-helper.js';
import {TEST_RESULT_FILE_NAME, ZIP} from '../../constants/app-constants.js';
import { OUTPUT_FORMAT_NOT_SUPPORTED_ERROR } from '../../constants/message-constants.js';


const isDirOrFileExists = (filePath: string): boolean => {
	return fs.existsSync(filePath);
};

const isDirectory = (filePath: string): boolean => {
	if (!isDirOrFileExists(filePath)) {
		return false;
	}
	return fs.statSync(filePath).isDirectory();
};

const isSubDirectoryExists = (parentDirPath: string, folderName: string): boolean => {
	const subDirectoryPath = path.join(parentDirPath, folderName);
	return isDirectory(subDirectoryPath);
};

const getSubDirectory = (parentDirPath: string, folderName: string): string => {
	if (!isSubDirectoryExists(parentDirPath, folderName)) {
		throw new Error(`The folder '${folderName}' is invalid or does not exist inside parent directory '${parentDirPath}'`);
	}
	return path.join(parentDirPath, folderName);
};

const isFile = (filePath: string): boolean => {
	if (!isDirOrFileExists(filePath)) {
		return false;
	}
	return fs.statSync(filePath).isFile();
};

// Assisted by WCA@IBM
// Latest GenAI contribution: ibm/granite-20b-code-instruct-v2
/**
 * Reads a file from the specified directory and returns its contents as a string.
 * @param parentDirPath - The path to the directory where the file is located.
 * @param name - The name of the file to read.
 * @returns The contents of the file as a string.
 */
const readFile = (parentDirPath: string, name: string): string => {
	const filePath = path.join(parentDirPath, name);
	if (!isDirOrFileExists(filePath)) {
		throw new Error(`The file '${filePath}' does not exist`);
	}
	return fs.readFileSync(filePath, { encoding: 'utf-8'}).toString();
};

const hasReadAccess = (filePath: string): boolean => {
	try {
		fs.accessSync(filePath, fs.constants.R_OK);
		return true;
	} catch (error: unknown) {
		showError((error as Error).message);
		return false;
	}
};

const readFileAsBuffer = (filePath: string): Buffer => {
	return fs.readFileSync(filePath);
};

const getRandomFileName = (fileExtension = '.yml') => {
	if (!fileExtension.startsWith('.')) {
		fileExtension = `.${fileExtension}`;
	}
	return `${crypto.randomUUID()}${fileExtension}`;
};

const isYamlFile = (fileName: string) => {
	if (isNullOrUndefined(fileName)) {
		return false;
	}
	return fileName.endsWith('.yml') || fileName.endsWith('.yaml');
};

const isJsonFile = (fileName: string) => {
	if (isNullOrUndefined(fileName)) {
		return false;
	}
	return fileName.endsWith('.json');
};

const isOtherFile = (fileName: string) => {
	if (isNullOrUndefined(fileName)) {
		return false;
	}
	return !fileName.endsWith('.json') && !fileName.endsWith('.yaml');
};

const normalizePath = (inputPath: string)  => {
	if (!isDirOrFileExists(inputPath)) {
		throw new Error(`Invalid path: ${inputPath}`);
	}
	return path.normalize(inputPath);
};

const getParentDir = (filePath: string) => {
	if (!isDirOrFileExists(filePath)) {
		throw new Error(`Invalid path: ${filePath}`);
	}
	return path.dirname(filePath);
};

const readDirectoryContents = (folderPath: string): string[] => {
	return fs.readdirSync(folderPath);
};

const readFileAsString = (filePath: string): string => {
	if (!fs.existsSync(filePath)) {
		throw new Error(`The file '${filePath}' does not exist`);
	}
	return fs.readFileSync(filePath, 'utf8');
};

const getFileNameFromPath = (filePath : string) => {
	return path.basename(filePath);
}

const createBuildZip = async (zipBuffer: Buffer, zipFileName: string): Promise<boolean> => {
	try {
		if(!zipFileName.endsWith(ZIP)) {
			showError(OUTPUT_FORMAT_NOT_SUPPORTED_ERROR);
			return false;
		}
		await ensureDirectoryExists(path.dirname(zipFileName));
		fs.writeFileSync(zipFileName, zipBuffer);
		return true;
	} catch (error: unknown) {
		showError((error as Error).message);
		return false;
	}
};

const writeJsonToFile = async (filePath: string, data: object, options: { spaces?: number } = {}): Promise<void> => {
	try {
		await fs.writeJson(filePath, data, options);
	} catch (error) {
		throw new Error(`Failed to write JSON to file: ${(error as Error).message}`);
	}
};

const ensureDirectoryExists = async (dirPath: string): Promise<void> => {
	try {
		await fs.ensureDir(dirPath);
	} catch (error) {
		throw new Error(`Failed to ensure directory exists: ${(error as Error).message}`);
	}
};

const checkFileExists = async (filePath: string): Promise<boolean> => {
	try {
		return await fs.pathExists(filePath);
	} catch (error) {
		throw new Error(`Failed to check if file exists: ${(error as Error).message}`);
	}
};

const readJsonFromFile = async (filePath: string): Promise<any> => {
	try {
		return await fs.readJson(filePath);
	} catch (error) {
		throw new Error(`Failed to read JSON from file: ${(error as Error).message}`);
	}
};

export async function generateFileInRootDir(data: any) {
	const filePath = path.join(process.cwd(), TEST_RESULT_FILE_NAME);
	await writeJsonToFile(filePath, data, { spaces: 2 });
	showInfo(`Test results written to  ${filePath}`);
}

export {
	isDirOrFileExists,
	hasReadAccess,
	isDirectory,
	isFile,
	isSubDirectoryExists,
	readFile,
	readFileAsBuffer,
	getRandomFileName,
	isYamlFile,
	isJsonFile,
	isOtherFile,
	getSubDirectory,
	normalizePath,
	getParentDir,
	readDirectoryContents,
	readFileAsString,
	getFileNameFromPath,
	createBuildZip,
	writeJsonToFile,
	ensureDirectoryExists,
	checkFileExists,
	readJsonFromFile
};
