/**
* Copyright Super iPaaS Integration LLC, an IBM Company 2024
*/
import {hasReadAccess, isDirectory, isSubDirectoryExists, readFile} from '../common/fs-helper.js';
import {COMMA} from '../../constants/app-constants.js';
import {ProjectFileMetaData} from '../../model/studio/project-stu-model.js';
import {readYaml} from '../common/yaml-helper.js';
import {isNullOrUndefined} from '../common/data-helper.js';
import {INVALID_PROJECT_NAMES, MULTIPLE_PROJECTS_SPECIFIED} from '../../constants/message-constants.js';
import { showInfo } from '../common/message-helper.js';

const STUDIO_METADATA_FILE_NAME = '.apistudio-projects';

// returns comma seperated string of project names
const getAllProjectNames = (rootDirPath: string): string => {
	/* (1) Check the root directory */
	checkIfRootDirExists(rootDirPath);
	checkForRootDirPermission(rootDirPath);
    
	/* (2) Read the studio project metadata file */
	const projectNames: string[] = readProjectsFromMetaData(rootDirPath);
    
	/* (3) Join as comma seperated string and return */
	return projectNames.join(COMMA);
};

/*
	@param: rootDirPath
	@param: projects - comma seperated project names.
	@returns: Set of other project names, or null if projects contains all projects
 */
const getOtherProjectsNames = (rootDirPath: string, projects: string): Set<string>|null => {

	const allProjects = new Set<string>(getAllProjectNames(rootDirPath).split(COMMA));

	if (isNullOrUndefined(projects)) {
		return allProjects;
	}

	const currentProjects = new Set<string>(projects.split(COMMA));
	// Check if currentProjects contains all projects from allProjects
	if (currentProjects.size === allProjects.size) {
		let allMatch = true;
		for (const project of allProjects) {
			if (!currentProjects.has(project)) {
				allMatch = false;
				break;
			}
		}
		if (allMatch) {
			showInfo('All projects are included, returning null');
			return null;
		}
	}

	const clonedProjects = new Set(allProjects);
	currentProjects.forEach(currProject => clonedProjects.delete(currProject));
	return clonedProjects;
};

const readProjectsFromMetaData = (rootDirPath: string): string[] => {
	const fileContent = readFile(rootDirPath, STUDIO_METADATA_FILE_NAME);
	const projectMetadata: ProjectFileMetaData = readYaml<ProjectFileMetaData>(fileContent);
	return Object.keys(projectMetadata.projects);
};

const checkIfRootDirExists = (rootDirPath: string): boolean  => {
	const isExists = isDirectory(rootDirPath);
	if (!isExists) {
		throw new Error(`The local directory path '${rootDirPath}' is either not a directory or does not exist.`);
	}
	return isExists;
};

const checkForRootDirPermission = (rootDirPath: string): boolean => {
	const hasAccess = hasReadAccess(rootDirPath);
	if(!hasAccess) {
		throw new Error(`No read access for the path: ${rootDirPath}`);
	}
	return hasAccess;
};

const checkIfAllProjectExists  = (rootDir: string, projectNames: string): boolean => {
	if (isNullOrUndefined(projectNames)) {
		throw new Error(INVALID_PROJECT_NAMES);
	}
	const projects = projectNames.split(COMMA);
	for (const project of projects) {
		checkIfProjectDirExists(rootDir, project);
	}
	return true;
};

const checkIfProjectExists = (rootDir: string, projectName: string): boolean => {
	if (isNullOrUndefined(projectName)) {
		throw new Error(INVALID_PROJECT_NAMES);
	}
	if(projectName.includes(COMMA)) {
		throw new Error(MULTIPLE_PROJECTS_SPECIFIED);
	}
	checkIfProjectDirExists(rootDir, projectName);
	return true;
};

const checkIfProjectDirExists = (rootDirPath: string, projectFolderName: string) => {
	const isExists = isSubDirectoryExists(rootDirPath, projectFolderName);
	if (!isExists) {
		throw new Error(`The project '${projectFolderName}' does not exist in the local directory '${rootDirPath}'`);
	}
	return isExists;
};

export {getAllProjectNames, checkIfRootDirExists, checkForRootDirPermission, getOtherProjectsNames, checkIfAllProjectExists, checkIfProjectExists};
