// @ts-check
import { getCurrentState } from "@rnx-kit/tools-react-native/cache";
import { loadContextAsync } from "@rnx-kit/tools-react-native/context";
import * as fs from "node:fs";
import * as path from "node:path";
import {
  isMain,
  readJSONFile,
  readTextFile,
  writeTextFile,
} from "../scripts/helpers.js";

/**
 * @typedef {import("@react-native-community/cli-types").Config} Config
 * @typedef {import("../scripts/types.js").AndroidDependencies} AndroidDependencies
 */

/**
 * @see {@link https://github.com/facebook/react-native/blob/924fb3de9bc9328c3315316fbb796b933be5bcbe/packages/react-native-gradle-plugin/shared/src/main/kotlin/com/facebook/react/model/ModelAutolinkingDependenciesJson.kt#L17}
 * @param {string} name
 * @returns {string}
 */
export function cleanDependencyName(name) {
  const nom = name.replace(/[!'()*/~]+/g, "_");
  return nom.startsWith("@") ? nom.substring(1) : nom;
}

/**
 * @param {string} p
 */
function ensureDirForFile(p) {
  fs.mkdirSync(path.dirname(p), { recursive: true, mode: 0o755 });
}

/**
 * @param {Config} config
 * @returns {AndroidDependencies}
 */
export function pickAndroidDependencies({ dependencies }) {
  /** @type {AndroidDependencies} */
  const result = {};
  for (const dependency of Object.values(dependencies)) {
    const { android } = dependency.platforms;
    const projectDir = android?.sourceDir;
    if (projectDir && android?.isPureCxxDependency != true) {
      const name = ":" + cleanDependencyName(dependency.name);

      /** @type {AndroidDependencies[string]["configurations"]} */
      const configurations = [];
      const dependencyConfiguration = android.dependencyConfiguration;
      if (dependencyConfiguration) {
        configurations.push(dependencyConfiguration);
      } else {
        const buildTypes = android.buildTypes ?? [];
        if (buildTypes.length === 0) {
          configurations.push("implementation");
        } else {
          for (const buildType of buildTypes) {
            configurations.push(`${buildType}Implementation`);
          }
        }
      }

      result[name] = { projectDir, configurations };
    }
  }
  return result;
}

/**
 * @param {Config} config
 */
export function pruneDependencies(config) {
  const { dependencies } = config;
  for (const [name, dependency] of Object.entries(dependencies)) {
    if (!Object.values(dependency.platforms).some((entry) => Boolean(entry))) {
      delete dependencies[name];
    }
  }
  return config;
}

/**
 * @param {string} json
 * @param {string} projectRoot
 * @returns {Promise<Config>}
 */
async function loadConfig(json, projectRoot) {
  const state = getCurrentState(projectRoot);
  const stateFile = json.substring(0, json.length - "json".length) + "sha256";
  if (fs.existsSync(stateFile) && readTextFile(stateFile) === state) {
    return readJSONFile(json);
  }

  const config = await loadContextAsync(projectRoot);
  const prunedConfig = pruneDependencies(config);

  ensureDirForFile(json);
  writeTextFile(json, JSON.stringify(prunedConfig, undefined, 2) + "\n");
  writeTextFile(stateFile, state);
  return prunedConfig;
}

/**
 * @param {string} projectRoot
 * @param {string} output
 * @returns {Promise<void>}
 */
async function main(projectRoot, output) {
  const config = await loadConfig(
    output.replace(
      /[/\\]app[/\\]build[/\\]generated[/\\]rnta[/\\]/,
      "/build/generated/autolinking/"
    ),
    projectRoot
  );
  const dependencies = pickAndroidDependencies(config);

  const json = JSON.stringify(dependencies, undefined, 2);
  if (!output) {
    console.log(json);
  } else {
    ensureDirForFile(output);
    writeTextFile(output, json + "\n");
  }
}

if (isMain(import.meta.url)) {
  const [, , projectRoot = process.cwd(), output = ""] = process.argv;
  await main(projectRoot, output);
}
