/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LOG_MODULE_TYPE} from 'arkanalyzer/lib/utils/logger';
import {Logger} from 'arkanalyzer';
import {Command, OptionValues} from 'commander';
import {FileUtils} from '../../utils/common/FileUtils';
import fs from 'fs';
import {ANALYSIS_MODE} from './utils';

const logger = Logger.getLogger(LOG_MODULE_TYPE.TOOL, 'DependencyAnalysisCliParser');
const SUPPORTED_EXTENSIONS: string[] = ['.ts', '.ets', '.js'];

// 定义命令行参数接口
export interface CommandLineArgs {
    mode: string;
    projectPath: string;
    sdkPath: string;
    cSdkPaths: string[];
    outputDir: string;
    sourceFiles: string[]
}

// get all the file paths from the file list path
function getFileList(path: string): string[] {
    let fileList: string[] = [];
    try {
        const jsonData = JSON.parse(fs.readFileSync(path, 'utf8'));
        fileList.push(...((jsonData?.sourceFiles ?? []) as string[]));
    } catch (error) {
        logger.error(`Error occurred while reading file list from ${path}: ${error}`);
    }
    return fileList;
}

// generate CommandLineArgs from config file
export function genArgsByConfig(configPath: string): CommandLineArgs {
    let jsonData;
    try {
        jsonData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
    } catch (error) {
        logger.error(`Error occurred while reading config from ${configPath}: ${error}`);
        process.exit(1);
    }

    if (!jsonData.mode) {
        jsonData.mode = 'all';
        logger.warn(`Analysis mode is not set, bu default set to 'all'.`);
    }
    if (!ANALYSIS_MODE.has(jsonData.mode.toLowerCase())) {
        logger.error(`Invalid mode set in config file: ${jsonData.mode}`);
        process.exit(1);
    }

    if (!jsonData || !jsonData.projectPath || !jsonData.sdkPath) {
        logger.error(`The configurations in ${configPath} is not valid, please check.`);
        process.exit(1);
    }
    logger.info(`Parsed config file: ${JSON.stringify(jsonData)}`);
    let files: string[];
    if (jsonData.analysisFilesPath) {
        files = getFileList(jsonData.analysisFilesPath);
    } else {
        const fileDir = jsonData.analysisDir ? jsonData.analysisDir : jsonData.projectPath;
        files = FileUtils.getAllFiles(fileDir, SUPPORTED_EXTENSIONS);
    }

    return {
        mode: jsonData.mode.toLowerCase(),
        projectPath: jsonData.projectPath,
        sdkPath: jsonData.sdkPath,
        cSdkPaths: jsonData.cSdkPaths ?? [],
        outputDir: jsonData.outputDir ?? './out',
        sourceFiles: files
    };
}

// get cli options
function getCliOptions(program: Command, args: string[]): OptionValues {
    program
        .option('--mode [mode]', 'run mode, by default analyze all')
        .option('--configPath [configPath]', 'basic config path')
        .option('--projectPath [projectPath]', 'project path')
        .option('--sdkPath [sdkPath]', 'sdk path')
        .option('--cSdkPaths [cSdkPaths...]', 'c sdk paths')
        .option('--outputDir [outputDir]', 'output directory')
        .option('--analysisFilesPath [analysisFilesPath]', 'path of source files to be analyzed')
        .option('--analysisDir [analysisDir]', 'directory to be analyzed, by default use project root')
        .parse(args);
    return program.opts();
}

// 解析命令行参数
export function parseCliArgs(): CommandLineArgs {
    logger.info('Parsing cli options...');
    const program = new Command();
    const options = getCliOptions(program, process.argv);
    logger.info(`Parsed cli options: ${JSON.stringify(options)}`);

    // if configPath is enabled, other args would be ignored
    if (options.configPath) {
        logger.info(`Parsing configuration from configFile: ${options.configPath}`);
        return genArgsByConfig(options.configPath);
    }

    if (!options.projectPath || !options.sdkPath) {
        logger.error(`Invalid command line options, projectPath and sdkPath are required when configPath is not set.`);
        process.exit(1);
    }

    if (!options.mode) {
        options.mode = 'all';
        logger.warn(`Analysis mode is not set, bu default set to 'all'.`);
    }
    if (!ANALYSIS_MODE.has(options.mode.toLowerCase())) {
        logger.error(`Invalid mode set in CLI options: ${options.mode}`);
        process.exit(1);
    }

    let files: string[];
    if (options.analysisFilesPath) {
        files = getFileList(options.analysisFilesPath);
    } else {
        const fileDir = options.analysisDir ? options.analysisDir : options.projectPath;
        files = FileUtils.getAllFiles(fileDir, SUPPORTED_EXTENSIONS);
    }
    return {
        mode: options.mode.toLowerCase(),
        projectPath: options.projectPath,
        sdkPath: options.sdkPath,
        cSdkPaths: options.cSdkPaths ?? [],
        outputDir: options.outputDir ?? './out',
        sourceFiles: files
    };
}