/*
 * Copyright (c) 2023 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 { expect } from 'chai';
import path from 'path';
import fs from 'fs';
import { FileUtils } from '../../src/utils/FileUtils';
import { FilesMap, Parser } from '../../src/coreImpl/parser/parser';
import { StringConstant } from '../../src/utils/Constant';
import { DiffHelper } from '../../src/coreImpl/diff/diff';
import { BasicDiffInfo } from '../../src/typedef/diff/ApiInfoDiff';
import { ApiStatisticsHelper } from '../../src/coreImpl/statistics/Statistics';
import { ApiStatisticsInfo } from '../../src/typedef/statistics/ApiStatistics';
import { Check } from '../../src/coreImpl/checker/src/api_check_plugin';
import { apiCheckResult, cleanApiCheckResult, compositiveResult } from '../../src/utils/checkUtils';
import { LocalEntry } from '../../src/coreImpl/checker/local_entry';
import { ApiChangeCheck } from '../../src/coreImpl/checker/src/check_api_diff';
import { ApiResultMessage, checkEntryType } from '../../src/typedef/checker/result_type';
const utDir: string = 'test/ut';
const outputDir: string = 'test/output';
const expectDir: string = 'test/expect';

describe('testParserEachSince', function () {
  before(() => { Parser.cleanParserParamSDK(); });
  const testFileDir: string = path.join(FileUtils.getBaseDirName(), utDir, 'parserSince');
  const outputFileDir: string = path.join(FileUtils.getBaseDirName(), outputDir, 'parserSince');
  const expectFileDir: string = path.join(FileUtils.getBaseDirName(), expectDir, 'parserSince');
  const testFileNames: string[] = fs.readdirSync(testFileDir);
  testFileNames.forEach((testFileName: string) => {
    const baseName: string = path
      .basename(testFileName)
      .replace(/.d.ts/g, '')
      .replace(/.d.ets/g, '');
    const testFilePath: string = path.join(testFileDir, testFileName);
    const outputFilePath: string = path.join(outputFileDir, `${baseName}.json`);
    const expectFilePath: string = path.join(expectFileDir, `${baseName}.json`);
    it('\ntestFile#' + testFilePath + '\noutput:' + outputFilePath + '\nexpect:' + expectFilePath, function () {
      if (fs.existsSync(outputFilePath)) {
        fs.rmdirSync(outputFilePath, { recursive: true });
      }
      if (!fs.existsSync(outputFileDir)) {
        fs.mkdirSync(outputFileDir);
      }
      const outputContent: string = Parser.getParseEachSince(Parser.parseFile(testFileDir, testFilePath));
      fs.writeFileSync(outputFilePath, outputContent, StringConstant.UTF8);
      const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
      expect(outputContent).eql(expectFileContent);
    });
  });
});

describe('testParser', function () {
  before(() => { Parser.cleanParserParamSDK(); });
  const testFileDir: string = path.join(FileUtils.getBaseDirName(), utDir, 'parser');
  const outputFileDir: string = path.join(FileUtils.getBaseDirName(), outputDir, 'parser');
  const expectFileDir: string = path.join(FileUtils.getBaseDirName(), expectDir, 'parser');
  const testFileNames: string[] = fs.readdirSync(testFileDir);
  testFileNames.forEach((testFileName: string) => {
    const baseName: string = path
      .basename(testFileName)
      .replace(/.d.ts/g, '')
      .replace(/.d.ets/g, '');
    const testFilePath: string = path.join(testFileDir, testFileName);
    const outputFilePath: string = path.join(outputFileDir, `${baseName}.json`);
    const expectFilePath: string = path.join(expectFileDir, `${baseName}.json`);
    it('\ntestFile#' + testFilePath + '\noutput:' + outputFilePath + '\nexpect:' + expectFilePath, function () {
      if (fs.existsSync(outputFilePath)) {
        fs.rmdirSync(outputFilePath, { recursive: true });
      }
      if (!fs.existsSync(outputFileDir)) {
        fs.mkdirSync(outputFileDir);
      }
      const outputContent: string = Parser.getParseResults(Parser.parseFile(testFileDir, testFilePath));
      fs.writeFileSync(outputFilePath, outputContent, StringConstant.UTF8);
      const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
      expect(outputContent).eql(expectFileContent);
    });
  });
});

describe('testApiDiff', function () {
  before(() => { Parser.cleanParserParamSDK(); });
  const testOldFileDir: string = path.join(FileUtils.getBaseDirName(), utDir, 'apiDiff/old');
  const testNewFileDir: string = testOldFileDir.replace('old', 'new');
  const outputFileDir: string = path.join(FileUtils.getBaseDirName(), outputDir, 'apiDiff');
  const expectFileDir: string = path.join(FileUtils.getBaseDirName(), expectDir, 'apiDiff');
  const testOldFileNames: string[] = fs.readdirSync(testOldFileDir);
  testOldFileNames.forEach((testOldFileName: string) => {
    const baseName: string = path
      .basename(testOldFileName)
      .replace(/.d.ts/g, '')
      .replace(/.d.ets/g, '');
    const testOldFilePath: string = path.join(testOldFileDir, testOldFileName);
    const testNewFilePath: string = testOldFilePath.replace('old', 'new');
    const outputFilePath: string = path.join(outputFileDir, `${baseName}.json`);
    const expectFilePath: string = path.join(expectFileDir, `${baseName}.json`);
    it('\ntestFile#' + testOldFilePath + '\noutput:' + outputFilePath + '\nexpect:' + expectFilePath, function () {
      if (fs.existsSync(outputFilePath)) {
        fs.rmdirSync(outputFilePath, { recursive: true });
      }
      if (!fs.existsSync(outputFileDir)) {
        fs.mkdirSync(outputFileDir);
      }
      Parser.cleanParserParamSDK();
      const oldSDKApiMap: FilesMap = Parser.parseFile(testOldFileDir, testOldFilePath);
      Parser.cleanParserParamSDK();
      const newSDKApiMap: FilesMap = Parser.parseFile(testNewFileDir, testNewFilePath);
      const diffInfos: BasicDiffInfo[] = DiffHelper.diffSDK(oldSDKApiMap, newSDKApiMap, true);
      const outputContent: string = JSON.stringify(diffInfos, null, 2);
      fs.writeFileSync(outputFilePath, outputContent, StringConstant.UTF8);
      const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
      expect(outputContent).eql(expectFileContent);
    });
  });
});

describe('testStatistics', function () {
  before(() => { Parser.cleanParserParamSDK(); });
  const testFileDir: string = path.join(FileUtils.getBaseDirName(), utDir, 'apiStatistics');
  const outputFileDir: string = path.join(FileUtils.getBaseDirName(), outputDir, 'apiStatistics');
  const expectFileDir: string = path.join(FileUtils.getBaseDirName(), expectDir, 'apiStatistics');
  const testFileNames: string[] = fs.readdirSync(testFileDir);
  testFileNames.forEach((testFileName: string) => {
    const baseName: string = path
      .basename(testFileName)
      .replace(/.d.ts/g, '')
      .replace(/.d.ets/g, '');
    const testFilePath: string = path.join(testFileDir, testFileName);
    const outputFilePath: string = path.join(outputFileDir, `${baseName}.json`);
    const expectFilePath: string = path.join(expectFileDir, `${baseName}.json`);
    it('\ntestFile#' + testFilePath + '\noutput:' + outputFilePath + '\nexpect:' + expectFilePath, function () {
      if (!fs.existsSync(outputFileDir)) {
        fs.mkdirSync(outputFileDir);
      }
      const apiInfos: ApiStatisticsInfo[] = ApiStatisticsHelper.getApiStatisticsInfos(
        Parser.parseFile(testFileDir, testFilePath)
      ).apiStatisticsInfos;
      const outputContent: string = JSON.stringify(apiInfos, null, 2);
      fs.writeFileSync(outputFilePath, outputContent, StringConstant.UTF8);
      const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
      expect(outputContent).eql(expectFileContent);
    });
  });
});

describe('testApiCheck', function () {
  before(() => { Parser.cleanParserParamSDK(); });
  const testFileDir: string = path.join(FileUtils.getBaseDirName(), utDir, 'apiCheck');
  const outputFileDir: string = path.join(FileUtils.getBaseDirName(), outputDir, 'apiCheck');
  const expectFileDir: string = path.join(FileUtils.getBaseDirName(), expectDir, 'apiCheck');
  const testFileNames: string[] = fs.readdirSync(testFileDir);
  testFileNames.forEach((testFileName: string) => {
    const baseName: string = path
      .basename(testFileName)
      .replace(/.d.ts/g, '')
      .replace(/.d.ets/g, '');
    const testFilePath: string = path.join(testFileDir, testFileName);
    const outputFilePath: string = path.join(outputFileDir, `${baseName}.json`);
    const expectFilePath: string = path.join(expectFileDir, `${baseName}.json`);
    it('\ntestFile#' + testFilePath + '\noutput:' + outputFilePath + '\nexpect:' + expectFilePath, function () {
      if (!fs.existsSync(outputFileDir)) {
        fs.mkdirSync(outputFileDir);
      }
      Check.scanEntry([testFilePath], '');
      let ruleName: string = testFileName.substring(0, testFileName.indexOf('.'));
      ruleName = testFileName.indexOf('API_DEFINE_ANONYMOUS_FUNCTION') !== -1 ?
        'API_DEFINE_ANONYMOUS_FUNCTION_01' : ruleName;
      ruleName = testFileName.indexOf('API_DEFINE_HUMP_01_small') !== -1 ?
        'API_DEFINE_HUMP_01' : ruleName;
      ruleName = testFileName.indexOf('API_DEFINE_HUMP_02_LARGE') !== -1 ?
        'API_DEFINE_HUMP_02' : ruleName;
      ruleName = testFileName.indexOf('API_DEFINE_NAME_02_ability') !== -1 ?
        'API_DEFINE_NAME_02' : ruleName;

      LocalEntry.maskAlarm(compositiveResult, [ruleName]);
      apiCheckResult.forEach((result: ApiResultMessage) => {
        result.buggyFilePath = result.buggyFilePath.substring(result.buggyFilePath.indexOf('build-tools'), result.buggyFilePath.length);
      });
      const outputContent: string = JSON.stringify(apiCheckResult, null, 2);
      fs.writeFileSync(outputFilePath, outputContent, StringConstant.UTF8);
      const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
      expect(outputContent).eql(expectFileContent);
    });
  });
});

describe('testApiChangeCheck', function () {
  before(() => { Parser.cleanParserParamSDK(); });
  const testFileDir: string = path.join(FileUtils.getBaseDirName(), utDir, 'apiChange');
  const outputFileDir: string = path.join(FileUtils.getBaseDirName(), outputDir, 'apiChange');
  const expectFileDir: string = path.join(FileUtils.getBaseDirName(), expectDir, 'apiChange');
  const testOldFileDir: string = path.join(FileUtils.getBaseDirName(), utDir, 'apiChange/old');
  const testOldFileNames: string[] = fs.readdirSync(testOldFileDir);

  ApiChangeCheck.checkApiChange(testFileDir);
  testOldFileNames.forEach((testOldFileName: string) => {
    const baseName: string = path
      .basename(testOldFileName)
      .replace(/.d.ts/g, '')
      .replace(/.d.ets/g, '');
    const testOldFilePath: string = path.join(testOldFileDir, testOldFileName);
    const outputFilePath: string = path.join(outputFileDir, `${baseName}.json`);
    const expectFilePath: string = path.join(expectFileDir, `${baseName}.json`);
    it('\ntestFile#' + testOldFilePath + '\noutput:' + outputFilePath + '\nexpect:' + expectFilePath, function () {
      if (!fs.existsSync(outputFileDir)) {
        fs.mkdirSync(outputFileDir);
      }
      cleanApiCheckResult();
      LocalEntry.maskAlarm(compositiveResult, [baseName]);
      const keyString: string[] = [];
      const finalResult: ApiResultMessage[] = [];
      apiCheckResult.forEach(apiCheck => {
        const key: string = apiCheck.description + apiCheck.mainBuggyCode + apiCheck.extendInfo.hierarchicalRelations;
        if (!keyString.includes(key)) {
          keyString.push(key);
          finalResult.push(apiCheck);
        }
      });
      const outputContent: string = JSON.stringify(finalResult, null, 2);
      fs.writeFileSync(outputFilePath, outputContent, StringConstant.UTF8);
      const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
      expect(outputContent).eql(expectFileContent);
    });
  });

  describe('testApiCheckIncrement', function testApiCheckIncrement() {
    const testFileDir: string = path.join(FileUtils.getBaseDirName(), '/test/ut/apiIncrement');
    const outputFileDir: string = path.join(FileUtils.getBaseDirName(), '/test/output/apiIncrement');
    const expectFileDir: string = path.join(FileUtils.getBaseDirName(), '/test/expect/apiIncrement');
    const testFileNames: string[] = fs.readdirSync(testFileDir);

    testFileNames.forEach((testFileName: string) => {
      const testFilePath: string = path.join(testFileDir, testFileName);
      const testOldFileDir: string = path.join(testFilePath, '/old');
      const testNewFileDir: string = path.join(testFilePath, '/new');
      const baseName: string = path
        .basename(testFileName)
        .replace(/.d.ts/g, '')
        .replace(/.d.ets/g, '');
      const outputFilePath: string = path.join(outputFileDir, `${baseName}.json`);
      const expectFilePath: string = path.join(expectFileDir, `${baseName}.json`);
      it('\ntestFile#' + testFilePath + '\noutput:' + outputFilePath + '\nexpect:' + expectFilePath, function () {
        if (!fs.existsSync(outputFileDir)) {
          fs.mkdirSync(outputFileDir);
        }
        cleanApiCheckResult();
        const files: Array<string> = FileUtils.readFilesInDir(testNewFileDir);
        const checkParam: checkEntryType = {
          filePathArr: files,
          fileRuleArr: ['all'],
          output: './result.json',
          prId: testFilePath,
          isOutExcel: 'true',
          isIncrement: true,
        };
        LocalEntry.checkEntryLocal(checkParam);
        const outputContent: string = JSON.stringify(apiCheckResult, null, 2);
        fs.writeFileSync(outputFilePath, outputContent, StringConstant.UTF8);
        const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
        expect(outputContent).eql(expectFileContent);
      });
    });
  });