/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import fs from 'node:fs';
import tmp from 'tmp';
import {
  AbsolutePath,
  DescriptiveError,
  OHPackageJSON5,
  PackageJSON,
} from '../core';
import {
  createHarmonyDirContainsOnlyHarsCheck,
  createHarmonyDirExistsCheck,
  createOhPackageNameFollowsNamingConventionCheck,
  createNpmAndOhPackageVersionsAreEqualCheck,
} from './checks';
import { Check, CheckStatus } from './types';
import { RealFS, unpackTarGz } from '../io';

type StartProcessMessage = {
  type: 'START_PROCESS';
  name: string;
};

type FinishedCheckingMessage = {
  type: 'FINISHED_CHECKING';
  name: string;
  status: CheckStatus;
  message: string;
};

type VerificationMessage = StartProcessMessage | FinishedCheckingMessage;

export async function* verifyRNOHPackage({
  npmPackagePath,
  hasHarmonyCode,
  checkNamesToSkip,
}: {
  npmPackagePath: AbsolutePath;
  hasHarmonyCode: boolean;
  checkNamesToSkip: string[];
}): AsyncGenerator<VerificationMessage, void, unknown> {
  const npmPackageJson = PackageJSON.fromProjectRootPath(
    new RealFS(),
    npmPackagePath,
    npmPackagePath
  );
  const initialChecks: Check[] = [
    createHarmonyDirExistsCheck(npmPackagePath, hasHarmonyCode),
    createHarmonyDirContainsOnlyHarsCheck(npmPackagePath, hasHarmonyCode),
  ];
  yield* runChecks(initialChecks, checkNamesToSkip);
  if (hasHarmonyCode) {
    const harPath = findHarPath(npmPackagePath);
    if (!harPath) {
      throw new DescriptiveError({
        whatHappened: 'Attempted to verify har but no har was found',
        whatCanUserDo: [
          'Run this command without --no-harmony-code flag.',
          'Create har files by running DevEco Studio > Build > Make Module (MODULE_NAME). The har file can be later found in MODULE_NAME/build/default/outputs/default/MODULE_NAME.har. Copy that file to the PACKAGE_ROOT/harmony directory.',
        ],
      });
    }
    const tmpDirPath = new AbsolutePath(tmp.dirSync().name);
    yield {
      type: 'START_PROCESS',
      name: `Unpacking the har file to the tmpdir`,
    };
    await unpackTarGz(harPath, tmpDirPath);
    const ohPackageJson5 = OHPackageJSON5.fromPath(
      tmpDirPath.copyWithNewSegment('package', 'oh-package.json5')
    );
    const harChecks: Check[] = [
      createOhPackageNameFollowsNamingConventionCheck(
        npmPackageJson.name,
        ohPackageJson5.name
      ),
      createNpmAndOhPackageVersionsAreEqualCheck(
        npmPackageJson.version,
        ohPackageJson5.version
      ),
    ];
    yield* runChecks(harChecks, checkNamesToSkip);
  }
}

async function* runChecks(
  checks: Check[],
  checkNamesToSkip: string[]
): AsyncGenerator<VerificationMessage, void, unknown> {
  for (const { checkIf, run } of checks) {
    if (checkNamesToSkip.includes(checkIf)) {
      yield {
        type: 'FINISHED_CHECKING',
        name: checkIf,
        status: 'skip' as CheckStatus,
        message: '',
      };
      continue;
    }
    const result = await run();
    const status = typeof result === 'string' ? result : result.status;
    const message = typeof result === 'string' ? '' : result.message;
    yield { type: 'FINISHED_CHECKING', name: checkIf, status, message };
  }
}

function findHarPath(packagePath: AbsolutePath): AbsolutePath | null {
  const packageHarmonyPath = packagePath.copyWithNewSegment('harmony');
  const fileOrDirNames = fs.readdirSync(packageHarmonyPath.getValue());
  const harFilenames = fileOrDirNames.filter((fileOrDirName) =>
    fileOrDirName.endsWith('.har')
  );
  if (harFilenames.length != 1) {
    return null;
  }
  return packageHarmonyPath.copyWithNewSegment(harFilenames[0]);
}