///|
let xcode_minimum_version : String = "15.4"

///|
let swift_minimum_version : String = "5.0"

///|
let harmonyos_compatible_api : Int = 20

///|
let harmonyos_target_api : Int = 21

///|
let harmonyos_model_version : String = "6.0.1"

///|
fn xcode_developer_roots(selected_root : String) -> Array[String] {
  let home = @env.get_env_var("HOME").unwrap_or("")
  [
    @env.get_env_var("DEVELOPER_DIR").unwrap_or(""),
    selected_root,
    "/Applications/Xcode.app/Contents/Developer",
    "/Applications/Xcode-beta.app/Contents/Developer",
    join_path(home, "Applications/Xcode.app/Contents/Developer"),
    "/Library/Developer/CommandLineTools",
  ]
}

///|
fn has_valid_apple_sdk(root : String) -> Bool {
  if !path_is_dir(root) {
    return false
  }
  let entries : ArrayView[String] = @fs.read_dir(root) catch { _ => [] }
  entries.any(name => {
    path_is_nonempty_file(join_path(root, name + "/SDKSettings.json"))
  })
}

///|
fn xcode_root_supports_macos(root : String) -> Bool {
  has_valid_apple_sdk(
    join_path(root, "Platforms/MacOSX.platform/Developer/SDKs"),
  ) ||
  has_valid_apple_sdk(join_path(root, "SDKs"))
}

///|
fn xcode_root_supports_ios(root : String) -> Bool {
  has_valid_apple_sdk(
    join_path(root, "Platforms/iPhoneOS.platform/Developer/SDKs"),
  ) &&
  has_valid_apple_sdk(
    join_path(root, "Platforms/iPhoneSimulator.platform/Developer/SDKs"),
  )
}

///|
fn xcode_doctor_check(
  host : HostPlatform,
  macos_required : Bool,
  ios_required : Bool,
) -> DoctorCheck {
  if !macos_required && !ios_required {
    return skipped_check(
      "xcode", "Xcode is not required by the selected platforms",
    )
  }
  let xcodebuild = find_executable("xcodebuild", host)
  let xcrun = find_executable("xcrun", host)
  let xcode_select = find_executable("xcode-select", host)
  if xcodebuild is None || xcrun is None || xcode_select is None {
    return doctor_check(
      "xcode",
      Fail,
      true,
      "Xcode command-line tools are incomplete",
      detail="xcodebuild=" +
        xcodebuild.unwrap_or("missing") +
        " xcrun=" +
        xcrun.unwrap_or("missing") +
        " xcode-select=" +
        xcode_select.unwrap_or("missing"),
      remediation="Install Xcode and select it with xcode-select or DEVELOPER_DIR.",
    )
  }
  let selected_root = match
    run_doctor_probe(xcode_select.unwrap(), XcodeDeveloperPath) {
    { status: 0, output, } if output != "" => output
    probe =>
      return doctor_check(
        "xcode",
        Fail,
        true,
        "The selected Xcode developer directory could not be resolved",
        detail=if probe.output == "" {
          "xcode-select status=" + probe.status.to_string()
        } else {
          probe.output
        },
        remediation="Select a complete Xcode with xcode-select or DEVELOPER_DIR.",
      )
  }
  let xcode_version = match
    probe_version_floor(
      xcodebuild.unwrap(),
      DashVersion,
      "Xcode",
      [15, 4],
      "Xcode",
    ) {
    Ok(value) => value
    Err(message) =>
      return doctor_check(
        "xcode",
        Fail,
        true,
        "Xcode is below the 15.4 floor",
        detail=message,
        remediation="Install Xcode " +
          xcode_minimum_version +
          " or newer and select it with xcode-select or DEVELOPER_DIR.",
      )
  }
  let swift_version = match
    probe_version_floor(
      xcrun.unwrap(),
      SwiftCompilerVersion,
      "Apple Swift version",
      [5, 0],
      "Swift",
    ) {
    Ok(value) => value
    Err(message) =>
      return doctor_check(
        "xcode",
        Fail,
        true,
        "The selected Xcode does not provide Swift 5 language support",
        detail=message,
        remediation="Select a complete Xcode " +
          xcode_minimum_version +
          "+ installation that provides Swift " +
          swift_minimum_version +
          "+.",
      )
  }
  for root in xcode_developer_roots(selected_root) {
    if root != "" &&
      (!macos_required || xcode_root_supports_macos(root)) &&
      (!ios_required || xcode_root_supports_ios(root)) {
      return doctor_check(
        "xcode",
        Pass,
        true,
        if ios_required {
          "Xcode macOS and iOS SDKs are available"
        } else {
          "Xcode macOS SDK is available"
        },
        detail=root +
          " | xcode=" +
          xcode_version +
          " | swift=" +
          swift_version +
          " | " +
          xcodebuild.unwrap() +
          " | " +
          xcrun.unwrap(),
      )
    }
  }
  doctor_check(
    "xcode",
    Fail,
    true,
    if ios_required {
      "Xcode was found, but the iOS device/simulator SDKs are incomplete"
    } else {
      "Xcode command-line tools were found, but no macOS SDK was detected"
    },
    remediation="Install/select a complete Xcode developer directory.",
  )
}

///|
fn harmonyos_doctor_check(
  project_root : String,
  host : HostPlatform,
  required : Bool,
) -> DoctorCheck {
  if !required {
    return skipped_check(
      "harmonyos", "HarmonyOS is not required by the selected platforms",
    )
  }
  let home = @env.get_env_var("HOME").unwrap_or("")
  let sdk_candidates = [
    @env.get_env_var("HARMONYOS_SDK_HOME").unwrap_or(""),
    @env.get_env_var("OHOS_SDK_HOME").unwrap_or(""),
    "/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony",
    "/Applications/DevEco-Studio.app/Contents/sdk/default/hms",
    "/Applications/DevEco Studio.app/Contents/sdk/default/openharmony",
    "/Applications/DevEco Studio.app/Contents/sdk/default/hms",
    join_path(home, "Library/OpenHarmony/Sdk/21"),
    join_path(home, "Library/OpenHarmony/Sdk/20"),
  ]
  let sdk_root = Ref("")
  let toolchain_path = Ref("")
  for candidate in sdk_candidates {
    let toolchain = first_existing_file([
      join_path(candidate, "native/build/cmake/ohos.toolchain.cmake"),
      join_path(
        candidate, "default/openharmony/native/build/cmake/ohos.toolchain.cmake",
      ),
      join_path(
        candidate, "default/hms/native/build/cmake/ohos.toolchain.cmake",
      ),
      join_path(
        candidate, "openharmony/native/build/cmake/ohos.toolchain.cmake",
      ),
    ])
    match toolchain {
      Some(path) if path_is_nonempty_file(path) => {
        sdk_root.val = candidate
        toolchain_path.val = path
        break
      }
      _ => ()
    }
  }
  if sdk_root.val == "" {
    return doctor_check(
      "harmonyos",
      Fail,
      true,
      "HarmonyOS/OpenHarmony SDK root was not detected",
      remediation="Install DevEco Studio and set HARMONYOS_SDK_HOME.",
    )
  }
  let (sdk_api, sdk_model, sdk_metadata_path) = match
    harmony_sdk_metadata(sdk_root.val) {
    Ok(value) => value
    Err(message) =>
      return doctor_check(
        "harmonyos",
        Fail,
        true,
        "HarmonyOS SDK version metadata is incomplete",
        detail=message,
        remediation="Select a DevEco SDK with API 21 and platformVersion 6.0.1 or newer.",
      )
  }
  let sdk_model_version = match first_numeric_version(sdk_model) {
    Some(value) => value
    None =>
      return doctor_check(
        "harmonyos",
        Fail,
        true,
        "HarmonyOS SDK platformVersion could not be parsed",
        detail=sdk_model + " | " + sdk_metadata_path,
      )
  }
  if sdk_api < harmonyos_target_api ||
    !numeric_version_at_least(sdk_model_version, [6, 0, 1]) {
    return doctor_check(
      "harmonyos",
      Fail,
      true,
      "HarmonyOS SDK is below the API 21 / model 6.0.1 floor",
      detail="api=" +
        sdk_api.to_string() +
        " platformVersion=" +
        sdk_model +
        " | " +
        sdk_metadata_path,
      remediation="Install/select HarmonyOS SDK API 21 with model 6.0.1 or newer.",
    )
  }
  let project_contract = match harmony_project_contract(project_root) {
    Ok(value) => value
    Err(message) =>
      return doctor_check(
        "harmonyos",
        Fail,
        true,
        "HarmonyOS project deployment metadata is incompatible",
        detail=message,
        remediation="Use compatible API " +
          harmonyos_compatible_api.to_string() +
          ", target API " +
          harmonyos_target_api.to_string() +
          ", and modelVersion " +
          harmonyos_model_version +
          ".",
      )
  }
  let hvigor = first_executable([
    @env.get_env_var("HVIGORW").unwrap_or(""),
    "/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw",
    "/Applications/DevEco Studio.app/Contents/tools/hvigor/bin/hvigorw",
  ]).unwrap_or_else(() => find_executable("hvigorw", host).unwrap_or(""))
  let ohpm = first_executable([
    @env.get_env_var("OHPM").unwrap_or(""),
    "/Applications/DevEco-Studio.app/Contents/tools/ohpm/bin/ohpm",
    "/Applications/DevEco Studio.app/Contents/tools/ohpm/bin/ohpm",
  ]).unwrap_or_else(() => find_executable("ohpm", host).unwrap_or(""))
  let node = find_executable("node", host)
  let cmake = find_executable("cmake", host)
  let zip = find_executable("zip", host)
  if hvigor == "" || ohpm == "" || node is None || cmake is None || zip is None {
    return doctor_check(
      "harmonyos",
      Fail,
      true,
      "HarmonyOS build tools are incomplete",
      detail="hvigorw=" +
        (if hvigor == "" { "missing" } else { hvigor }) +
        " ohpm=" +
        (if ohpm == "" { "missing" } else { ohpm }) +
        " node=" +
        node.unwrap_or("missing") +
        " cmake=" +
        cmake.unwrap_or("missing") +
        " zip=" +
        zip.unwrap_or("missing"),
      remediation="Install DevEco Hvigor/ohpm plus Node.js, CMake, and zip; set HVIGORW/OHPM or add them to PATH.",
    )
  }
  let hdc = first_executable([
    join_path(sdk_root.val, "toolchains/hdc"),
    join_path(sdk_root.val, "toolchains/hdc.exe"),
    join_path(sdk_root.val, "../toolchains/hdc"),
    join_path(sdk_root.val, "../toolchains/hdc.exe"),
  ]).unwrap_or_else(() => find_executable("hdc", host).unwrap_or(""))
  if hdc != "" {
    doctor_check(
      "harmonyos",
      Pass,
      true,
      "HarmonyOS SDK, Hvigor, ohpm, native tools, and hdc are available",
      detail="sdkApi=" +
        sdk_api.to_string() +
        " sdkModel=" +
        sdk_model +
        " " +
        project_contract +
        " | " +
        toolchain_path.val +
        " | " +
        hvigor +
        " | " +
        ohpm +
        " | " +
        hdc,
    )
  } else {
    doctor_check(
      "harmonyos",
      Warn,
      true,
      "HarmonyOS build tools are available, but hdc was not found",
      detail="sdkApi=" +
        sdk_api.to_string() +
        " sdkModel=" +
        sdk_model +
        " " +
        project_contract +
        " | " +
        toolchain_path.val,
      remediation="Add the SDK toolchains directory to PATH before device runtime testing.",
    )
  }
}