/**
 * 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 tmp from 'tmp';
import { ReactNativeFixture } from './ReactNativeFixture';
import { buildDirTree } from './fsUtils';
import { AbsolutePath, maybeMakeDirectories } from '../src/core';
import fs from 'node:fs';
import path from 'node:path';

let tmpDir: AbsolutePath;

beforeEach(async () => {
  const dir = tmp.dirSync();
  tmpDir = new AbsolutePath(dir.name);
});

afterEach(() => {
  if (expect.getState().assertionCalls != expect.getState().numPassingAsserts) {
    console.log(buildDirTree(tmpDir.getValue()));
  }
});

function createSampleSpecFiles(
  specDirPath: AbsolutePath,
  componentNames: string[],
  moduleNames: string[]
) {
  maybeMakeDirectories(specDirPath);
  componentNames.forEach((componentName) => {
    fs.writeFileSync(
      specDirPath
        .copyWithNewSegment(`${componentName}NativeComponent.ts`)
        .getValue(),
      createViewComponentSpec(componentName)
    );
  });
  moduleNames.forEach((moduleName) => {
    fs.writeFileSync(
      specDirPath.copyWithNewSegment(`Native${moduleName}.ts`).getValue(),
      createTurboModuleSpec(moduleName)
    );
  });
}

function createViewComponentSpec(name: string) {
  return `
import { ViewProps, HostComponent } from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type { Float } from 'react-native/Libraries/Types/CodegenTypes';

export interface ${name}NativeProps extends ViewProps {
  size: Float;
}

export default codegenNativeComponent<${name}NativeProps>(
  '${name}'
) as HostComponent<${name}NativeProps>;
  `;
}

function createTurboModuleSpec(name: string) {
  return `
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
import { TurboModuleRegistry } from 'react-native';

interface Spec extends TurboModule {
  runProcedure(): void;
  getBool(arg: boolean): boolean;
  getString(arg: string): string;
  getObject(arg: { x: { y: number } }): Object;
  getArrayOfNumbersAsync(): Promise<number[]>;
}

export default TurboModuleRegistry.get<Spec>("${name}")!;
`;
}

function getFileNamesFromDirPath(dirPath: AbsolutePath): string[] {
  try {
    if (!fs.existsSync(dirPath.getValue())) {
      return [];
    }
    const files: string[] = fs.readdirSync(dirPath.getValue());
    const fileNames: string[] = files.filter((file) => {
      const filePath: string = path.join(dirPath.getValue(), file);
      return fs.statSync(filePath).isFile();
    });
    return fileNames;
  } catch (err) {
    console.error('Error reading directory:', err);
    throw err;
  }
}

it('should export codegen-lib-harmony command', () => {
  const helpDescription = new ReactNativeFixture(tmpDir).codegenLibHarmony({
    help: true,
  });
  expect(helpDescription.includes('codegen-lib-harmony')).toBeTruthy();
});

it('should generate components and Turbo Module code to the desired output directory based on provided paths to specs', () => {
  const specsDirPath = tmpDir.copyWithNewSegment('specs');
  createSampleSpecFiles(specsDirPath, ['MyView', 'MyView2'], ['MyModule']);
  const cppOutputPath = tmpDir.copyWithNewSegment('codegen', 'cpp');
  const etsOutputPath = tmpDir.copyWithNewSegment('codegen', 'ets');

  new ReactNativeFixture(tmpDir).codegenLibHarmony({
    npmPackageName: 'react-native-harmony-foobar',
    cppOutputPath: cppOutputPath.relativeTo(tmpDir).getValue(),
    etsOutputPath: etsOutputPath.relativeTo(tmpDir).getValue(),
    cppComponentsSpecPaths: specsDirPath.relativeTo(tmpDir).getValue(),
    arktsComponentsSpecPaths: specsDirPath.relativeTo(tmpDir).getValue(),
    turboModulesSpecPaths: specsDirPath.relativeTo(tmpDir).getValue(),
    noSafetyCheck: true,
  });

  expect(
    getFileNamesFromDirPath(
      cppOutputPath.copyWithNewSegment('RNOH', 'generated', 'components')
    ).length
  ).toBeGreaterThanOrEqual(4);
  expect(
    getFileNamesFromDirPath(
      cppOutputPath.copyWithNewSegment('RNOH', 'generated', 'turbo_modules')
    ).length
  ).toBeGreaterThan(0);
  expect(
    getFileNamesFromDirPath(
      cppOutputPath.copyWithNewSegment(
        'react',
        'renderer',
        'components',
        'react_native_harmony_foobar'
      )
    ).length
  ).toBeGreaterThan(0);
  expect(
    getFileNamesFromDirPath(etsOutputPath.copyWithNewSegment('components'))
      .length
  ).toBeGreaterThan(0);
  expect(
    getFileNamesFromDirPath(etsOutputPath.copyWithNewSegment('turboModules'))
      .length
  ).toBeGreaterThan(0);
});