/**
 * 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 { ValidationError } from '../src/tasks/sync/SyncTask';
import { memfs, NestedDirectoryJSON } from 'memfs';
import pathUtils from 'node:path';
import { StubCommandExecutor, FakeLogger, FakeSyncTask } from './__fixtures__';

const ORIGINAL_DEVECO_SDK_HOME = process.env.DEVECO_SDK_HOME;

beforeEach(() => {
  process.env.DEVECO_SDK_HOME = '/Applications/DevEco-Studio.app/Contents/sdk';
});

afterEach(() => {
  process.env.DEVECO_SDK_HOME = ORIGINAL_DEVECO_SDK_HOME;
});

function createSyncTask(options: {
  onCodegenRun?: (command: string) => string;
  fsSetup?: NestedDirectoryJSON;
}) {
  const fakeCliExecutor = new StubCommandExecutor(
    options.onCodegenRun ?? (() => '')
  );
  const fakeLogger = new FakeLogger();

  const SyncTask = new FakeSyncTask(
    fakeCliExecutor,
    fakeLogger,
    memfs(options.fsSetup, './').fs
  );
  return { SyncTask, fakeCliExecutor, fakeLogger };
}

it('should handle port forwarding, call codegen-harmony, and log progress', () => {
  const { SyncTask, fakeCliExecutor, fakeLogger } = createSyncTask({
    onCodegenRun: () => '__GENERATED_FILE__',
    fsSetup: {
      node_modules: {
        '.bin': {
          'react-native': '',
        },
      },
    },
  });

  SyncTask.run({
    nodeModulesPath: './node_modules',
    codegen: { rnohModulePath: '_' },
  });

  const executedCommands = fakeCliExecutor.getCommands();
  expect(executedCommands[0]).toContain(
    '"/test/path" rport tcp:8081 tcp:8081'
  );
  expect(executedCommands[1]).toBe(
    pathUtils.join('node_modules', '.bin', 'react-native') +
      ` codegen-harmony --project-root-path .. --cpp-output-path entry${pathUtils.sep}src${pathUtils.sep}main${pathUtils.sep}cpp${pathUtils.sep}generated --ets-output-path _`
  );
  expect(
    fakeLogger
      .getLogs()
      .map((log) => log.msg)
      .includes('[codegen]\n__GENERATED_FILE__')
  ).toBeTruthy();
});

it("should fail if node_modules dir doesn't exist", () => {
  const { SyncTask } = createSyncTask({});

  expect(() => {
    SyncTask.run({
      nodeModulesPath: './NOT_EXISTING_DIR',
      codegen: { rnohModulePath: '_' },
    });
  }).toThrow(ValidationError);
});

it('should allow skipping codegen process', () => {
  const { SyncTask, fakeCliExecutor, fakeLogger } = createSyncTask({
    fsSetup: {
      node_modules: {
        '.bin': {
          'react-native': '',
        },
      },
    },
  });

  SyncTask.run({
    nodeModulesPath: './node_modules',
    codegen: null,
  });

  expect(
    fakeCliExecutor
      .getCommands()
      .some((command) => command.includes('codegen-harmony'))
  ).toBeFalsy();
  expect(
    fakeLogger.getLogs().some((log) => log.msg.includes('[codegen] skipped'))
  ).toBeTruthy();
});

it('should allow skipping Metro setup', () => {
  const { SyncTask, fakeCliExecutor, fakeLogger } = createSyncTask({
    fsSetup: {
      node_modules: {
        '.bin': {
          'react-native': '',
        },
      },
    },
  });

  SyncTask.run({
    nodeModulesPath: './node_modules',
    metro: null,
    codegen: null,
  });

  expect(
    fakeCliExecutor.getCommands().some((command) => command.includes('rport'))
  ).toBeFalsy();
  expect(
    fakeLogger.getLogs().some((log) => log.msg.includes('[metro] skipped'))
  ).toBeTruthy();
});

it('should call autolinking', () => {
  const { SyncTask, fakeCliExecutor } = createSyncTask({
    fsSetup: {
      node_modules: {
        '.bin': {
          'react-native': '',
        },
      },
    },
  });

  SyncTask.run({
    nodeModulesPath: './node_modules',
    codegen: null,
    metro: null,
  });

  expect(
    fakeCliExecutor
      .getCommands()
      .some((command) => command.includes('link-harmony'))
  ).toBeTruthy();
});

it('should skip autolinking', () => {
  const { SyncTask, fakeCliExecutor } = createSyncTask({
    fsSetup: {
      node_modules: {
        '.bin': {
          'react-native': '',
        },
      },
    },
  });

  SyncTask.run({
    nodeModulesPath: './node_modules',
    codegen: null,
    metro: null,
    autolinking: null,
  });

  expect(
    fakeCliExecutor
      .getCommands()
      .every((command) => !command.includes('link-harmony'))
  ).toBeTruthy();
});