/**
 * 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 pathUtils from "node:path";
import { execSync } from "child_process";
import { unlinkSync } from "fs";

const REPO_ROOT_PATH = pathUtils.resolve(__dirname, "..");
const RNOH_PKG_ROOT_PATH = pathUtils.join(
  REPO_ROOT_PATH,
  "packages",
  "react-native-harmony",
);

const REACT_NATIVE_SUBMODULE_PATH = pathUtils.join(
  REPO_ROOT_PATH,
  "packages",
  "react-native",
);

const REACT_NATIVE_PATCH_PATH = pathUtils.join(
  REPO_ROOT_PATH,
  "packages",
  "react-native.patch",
);

const BOOST_PATCH_PATH = pathUtils.join(
  REPO_ROOT_PATH,
  "packages",
  "boost.patch",
);

const BOOST_SUBMODULE_PATH = pathUtils.join(
  REPO_ROOT_PATH,
  "packages",
  "tester",
  "harmony",
  "react_native_openharmony",
  "src",
  "main",
  "cpp",
  "third-party",
  "boost",
);

const REACT_NATIVE_PKG_ROOT_PATH = pathUtils.join(
  REACT_NATIVE_SUBMODULE_PATH,
  "packages",
  "react-native",
);

const REACT_COMMON_SRC_PATH = pathUtils.join(
  REACT_NATIVE_PKG_ROOT_PATH,
  "ReactCommon",
);

const REACT_COMMON_DEST_PATH = pathUtils.join(
  REPO_ROOT_PATH,
  "packages",
  "tester",
  "harmony",
  "react_native_openharmony",
  "src",
  "main",
  "cpp",
  "third-party",
  "rn",
  "ReactCommon",
);

try {
  applyPatch();
  applyBoostPatch();
  syncJS("delegates");
  syncJS("Libraries");
  syncJS("src");
  // In RN 0.88+ the deprecated type declarations directory was renamed from
  // `types` to `types_DEPRECATED`; keep the RNOH package's own `types` dir name.
  syncJS("types_DEPRECATED", "types");
  syncCpp();
} finally {
  cleanReactNativeSubmodule();
}

function applyPatch() {
  cleanReactNativeSubmodule();
  execSync(`git apply --whitespace=fix ${REACT_NATIVE_PATCH_PATH}`, {
    cwd: REACT_NATIVE_SUBMODULE_PATH,
  });
}

/**
 * Apply packages/boost.patch to the boost submodule in-place.
 * Unlike react-native (flat submodule → reset --hard then apply), boost nests
 * libs/* as submodules, so a parent reset does not restore patched files.
 * Reverse-apply first (ignore if not previously applied), then apply — same
 * idempotency as RN clean+apply, without hardcoding paths from the patch.
 */
function applyBoostPatch() {
  if (!fs.existsSync(BOOST_PATCH_PATH)) {
    throw new Error(`missing boost patch: ${BOOST_PATCH_PATH}`);
  }
  if (!fs.existsSync(BOOST_SUBMODULE_PATH)) {
    throw new Error(`boost submodule not found: ${BOOST_SUBMODULE_PATH}`);
  }

  try {
    execSync(`git apply -R --whitespace=fix ${BOOST_PATCH_PATH}`, {
      cwd: BOOST_SUBMODULE_PATH,
      stdio: "pipe",
    });
  } catch (error) {
    const detail =
      error instanceof Error && "stderr" in error
        ? String(
            (error as { stderr?: Buffer | string }).stderr || error.message,
          )
        : String(error);
    console.log(
      `boost.patch reverse-apply skipped (likely not previously applied): ${detail.trim()}`,
    );
  }
  execSync(`git apply --whitespace=fix ${BOOST_PATCH_PATH}`, {
    cwd: BOOST_SUBMODULE_PATH,
  });
  console.log(
    `applied ${pathUtils.relative(REPO_ROOT_PATH, BOOST_PATCH_PATH)} to ${pathUtils.relative(REPO_ROOT_PATH, BOOST_SUBMODULE_PATH)}`,
  );
}

function cleanReactNativeSubmodule() {
  execSync("git reset --hard HEAD && git clean -dfx", {
    cwd: REACT_NATIVE_SUBMODULE_PATH,
  });
}

function syncJS(
  pathRelativeToRNRoot: string,
  destPathRelativeToRNOHRoot?: string,
) {
  const destPath = pathUtils.join(
    RNOH_PKG_ROOT_PATH,
    destPathRelativeToRNOHRoot ?? pathRelativeToRNRoot,
  );
  if (!fs.existsSync(destPath)) {
    fs.mkdirSync(destPath);
  }
  removeUntrackedFiles(destPath);
  fs.cpSync(
    pathUtils.join(REACT_NATIVE_PKG_ROOT_PATH, pathRelativeToRNRoot),
    destPath,
    { recursive: true },
  );
  console.log(`synchronized: ${pathUtils.relative(process.cwd(), destPath)}`);
}

function removeUntrackedFiles(dirPath: string): void {
  const stdout = execSync("git ls-files --others", {
    cwd: dirPath,
    encoding: "utf-8",
  });
  const files = stdout.split("\n").filter((file) => file);

  files.forEach((file) => {
    const fullPath = pathUtils.resolve(dirPath, file);
    unlinkSync(fullPath);
  });
}

function syncCpp() {
  if (!fs.existsSync(REACT_COMMON_DEST_PATH)) {
    fs.mkdirSync(REACT_COMMON_DEST_PATH);
  }
  removeUntrackedFiles(REACT_COMMON_DEST_PATH);
  fs.cpSync(REACT_COMMON_SRC_PATH, REACT_COMMON_DEST_PATH, { recursive: true });
  console.log(
    `synchronized: ${pathUtils.relative(process.cwd(), REACT_COMMON_DEST_PATH)}`,
  );
}