# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os
import tomllib
import shutil

BASE_PATH = os.path.join("src", "zen", "tests")
EXTERNAL_TESTS_MANIFEST = os.path.join(BASE_PATH, "manifest.toml")
EXTERNAL_TESTS_OUTPUT = os.path.join(BASE_PATH, "mochitests")

FILE_PREFIX = """
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# This file is autogenerated by scripts/import_external_tests.py
# Do not edit manually.

"""

BROWSER_MANIFEST_LIST_PREFIX = """
BROWSER_CHROME_MANIFESTS += [
"""

XPCSHELL_MANIFESTS_LIST_PREFIX = """
XPCSHELL_TESTS_MANIFESTS += [
"""

FILE_SUFFIX = "]"
VALID_MANIFEST_FILES = ["browser.toml", "xpcshell.toml"]


def get_tests_manifest():
  with open(EXTERNAL_TESTS_MANIFEST, "rb") as f:
    return tomllib.load(f)


def die_with_error(message):
  print(f"ERROR: {message}")
  exit(1)


def validate_tests_path(path, files, ignore_list):
  for ignore in ignore_list:
    if ignore not in files:
      die_with_error(f"Ignore file '{ignore}' not found in tests folder '{path}'")
  if not any(manifest_file in files for manifest_file in VALID_MANIFEST_FILES):
    die_with_error(f"None of the valid manifest files {VALID_MANIFEST_FILES} found in tests folder '{path}'")


def disable_and_replace_manifest(manifest, output_path):
  toml_file = None
  for manifest_file in VALID_MANIFEST_FILES:
    candidate = os.path.join(output_path, manifest_file)
    if os.path.exists(candidate):
      toml_file = candidate
      break
  disabled_tests = manifest.get("disable", [])
  with open(toml_file, "r") as f:
    data = f.read()
  for test in disabled_tests:
    segment = f'["{test}"]'
    if segment not in data:
      die_with_error(f"Could not disable test '{test}' as it was not found in '{toml_file}'")
    replace_with = f'["{test}"]\ndisabled="Disabled by import_external_tests.py"'
    data = data.replace(segment, replace_with)
  for replacement in manifest.get("replace-manifest", {}).keys():
    if replacement not in data:
      die_with_error(f"Could not replace manifest entry '{replacement}' as it was not found in '{toml_file}'")
    data = data.replace(replacement, manifest["replace-manifest"][replacement])
  with open(toml_file, "w") as f:
    f.write(data)


def import_test_suite(test_suite, source_path, output_path, ignore_list, manifest, is_direct_path=False):
  print(f"Importing test suite '{test_suite}' from '{source_path}'")
  tests_folder = os.path.join("engine", source_path)
  if not is_direct_path:
    tests_folder = os.path.join(tests_folder, "tests")
  if not os.path.exists(tests_folder):
    die_with_error(f"Tests folder not found: {tests_folder}")
  files = os.listdir(tests_folder)
  validate_tests_path(tests_folder, files, ignore_list)
  if os.path.exists(output_path):
    shutil.rmtree(output_path)
  os.makedirs(output_path, exist_ok=True)
  for item in files:
    if item in ignore_list:
      continue
    s = os.path.join(tests_folder, item)
    d = os.path.join(output_path, item)
    if os.path.isdir(s):
      shutil.copytree(s, d)
    else:
      shutil.copy2(s, d)
  disable_and_replace_manifest(manifest[test_suite], output_path)


def write_moz_build_file(manifest):
  moz_build_path = os.path.join(EXTERNAL_TESTS_OUTPUT, "moz.build")
  print(f"Writing moz.build file to '{moz_build_path}'")
  with open(moz_build_path, "w") as f:
    f.write(FILE_PREFIX)
    f.write(BROWSER_MANIFEST_LIST_PREFIX)
    for test_suite in manifest.keys():
      # add for browser.toml first
      if not manifest[test_suite].get("xpcshell", False):
        f.write(f'\t"{test_suite}/browser.toml",\n')
    f.write(FILE_SUFFIX)
    f.write(XPCSHELL_MANIFESTS_LIST_PREFIX)
    for test_suite in manifest.keys():
      # add for xpcshell.toml
      if manifest[test_suite].get("xpcshell", False):
        f.write(f'\t"{test_suite}/xpcshell.toml",\n')
    f.write(FILE_SUFFIX)


def make_sure_ordered_tests(manifest):
  ordered_tests = sorted(manifest.keys())
  if list(manifest.keys()) != ordered_tests:
    die_with_error("Test suites in manifest.toml are not in alphabetical order.")


def main():
  manifest = get_tests_manifest()
  if os.path.exists(EXTERNAL_TESTS_OUTPUT):
    shutil.rmtree(EXTERNAL_TESTS_OUTPUT)
  os.makedirs(EXTERNAL_TESTS_OUTPUT, exist_ok=True)

  make_sure_ordered_tests(manifest)
  for test_suite, config in manifest.items():
    import_test_suite(
        test_suite=test_suite,
        source_path=config["source"],
        output_path=os.path.join(EXTERNAL_TESTS_OUTPUT, test_suite),
        ignore_list=config.get("ignore", []),
        is_direct_path=config.get("is_direct_path", False),
        manifest=manifest
    )
  write_moz_build_file(manifest)


if __name__ == "__main__":
  main()
