/*
 * Copyright (c) 2022-2026 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 * as path from 'node:path';
import type { FileIssues, RuleFix } from 'homecheck';
import type { CommandLineOptions } from './CommandLineOptions';
import type { ProblemInfo } from './ProblemInfo';
import { FaultID } from './Problems';
import { shouldProcessFile } from './LinterRunner';
import { cookBookTag } from './CookBookMsg';

interface RuleConfigInfo {
  ruleSet: string[];
}

interface ProjectConfigInfo {
  projectName: string | undefined;
  projectPath: string | undefined;
  logPath: string;
  arkCheckPath: string;
  ohosSdkPath: string;
  hmsSdkPath: string;
  reportDir: string;
  languageTags: Map<string, number>;
  fileOrFolderToCheck: string[];
}

export function getHomeCheckConfigInfo(cmdOptions: CommandLineOptions): {
  ruleConfigInfo: RuleConfigInfo;
  projectConfigInfo: ProjectConfigInfo;
} {
  let inputFiles = cmdOptions.inputFiles;
  let fliesTocheck: string[] = inputFiles;
  if (cmdOptions.scanWholeProjectInHomecheck === true) {
    fliesTocheck = [];
  }
  inputFiles = inputFiles.filter((input) => {
    return shouldProcessFile(cmdOptions, input);
  });
  const languageTags = new Map<string, number>();
  inputFiles.forEach((file) => {
    languageTags.set(path.normalize(file), 2);
  });
  const ruleConfigInfo = {
    ruleSet: ['plugin:@migration/all'],
    files: ['**/*.ets', '**/*.ts', '**/*.js']
  };
  const projectConfigInfo = {
    projectName: cmdOptions.arktsWholeProjectPath,
    projectPath: cmdOptions.arktsWholeProjectPath,
    logPath: cmdOptions.outputFilePath ? path.join(cmdOptions.outputFilePath, 'HomeCheck.log') : './HomeCheck.log',
    arkCheckPath: './node_modules/homecheck',
    ohosSdkPath: cmdOptions.sdkDefaultApiPath ? cmdOptions.sdkDefaultApiPath : '',
    hmsSdkPath: cmdOptions.sdkExternalApiPath ? cmdOptions.sdkExternalApiPath[0] : '',
    reportDir: './',
    languageTags: languageTags,
    fileOrFolderToCheck: fliesTocheck,
    logLevel: cmdOptions.verbose ? 'DEBUG' : 'INFO',
    arkAnalyzerLogLevel: cmdOptions.verbose ? 'DEBUG' : 'ERROR'
  };
  return { ruleConfigInfo, projectConfigInfo };
}

export function transferIssues2ProblemInfo(fileIssuesArray: FileIssues[]): Map<string, ProblemInfo[]> {
  const result = new Map<string, ProblemInfo[]>();
  fileIssuesArray.forEach((fileIssues) => {
    fileIssues.issues.forEach((issueReport) => {
      const defect = issueReport.defect;
      const ruleTag = findRuleTagByDesc(defect.description);
      const problemInfo: ProblemInfo = {
        line: defect.reportLine,
        column: defect.reportColumn,
        endLine: defect.reportLine,
        endColumn: defect.reportColumn,
        start: 0,
        end: 0,
        type: '',
        severity: defect.severity,
        faultId: FaultID.LAST_ID,
        problem: defect.problem,
        suggest: '',
        rule: defect.description,
        ruleTag: ruleTag,
        autofixable: defect.fixable
      };
      if (problemInfo.autofixable) {
        const fix = issueReport.fix as RuleFix;
        const replacementText = fix.text;
        const start = fix.range[0];
        const end = fix.range[1];
        problemInfo.autofix = [{ replacementText, start, end }];
        problemInfo.autofixTitle = defect.ruleId;
      }
      const filePath = path.normalize(defect.mergeKey.split('%')[0]);
      const problems = result.get(filePath) || [];
      problems.push(problemInfo);
      result.set(filePath, problems);
    });
  });
  return result;
}

export function removeOutOfRangeFiles(
  homeCheckResult: Map<string, ProblemInfo[]>,
  cmdOptions: CommandLineOptions
): Map<string, ProblemInfo[]> {
  const filteredResult = new Map<string, ProblemInfo[]>();

  for (const [filePath, problems] of homeCheckResult) {
    if (cmdOptions.inputFiles.includes(filePath)) {
      filteredResult.set(filePath, problems);
    }
  }

  return filteredResult;
}

/*
 * ruleId is not reliable for matching, as one ruleId (e.g., "arkts-numeric-semantic") can cover multiple rules.
 * Using the rule name in description is more accurate.
 */
function findRuleTagByDesc(desc: string): number {
  const ruleNameMatch = desc.match(/\(([^)]+)\)/);

  if (ruleNameMatch) {
    const ruleName = ruleNameMatch[1];
    for (let i = 1; i < cookBookTag.length; i++) {
      if (cookBookTag[i]?.includes(ruleName)) {
        return i;
      }
    }
  }

  return -1;
}