// CLI configuration schema (XDG-backed). Stores user preferences that
// cannot be inferred from the project: SDK locations, default device, etc.
// The schema is intentionally minimal — every field is optional so a
// missing config file degrades gracefully into `default_cli_config()`.

///|
priv struct HarmonyosConfig {
  sdk_home : String?
  hvigorw : String?
  ohpm : String?
  signing_config_file : String?
}

///|
priv struct AndroidConfig {
  sdk_home : String?
  ndk_home : String?
  abi : Array[String]
  compile_sdk : Int?
  target_sdk : Int?
}

///|
priv struct IosConfig {
  sdk : String?
  arch : String?
  deployment_target : Int?
}

///|
priv struct PathsConfig {
  moon_home : String?
  moui_root : String?
  skia_root : String?
}

///|
priv struct CliConfig {
  schema_version : Int
  harmonyos : HarmonyosConfig
  android : AndroidConfig
  ios : IosConfig
  paths : PathsConfig
}

///|
fn default_harmonyos_config() -> HarmonyosConfig {
  { sdk_home: None, hvigorw: None, ohpm: None, signing_config_file: None, }
}

///|
fn default_android_config() -> AndroidConfig {
  {
    sdk_home: None,
    ndk_home: None,
    abi: [],
    compile_sdk: None,
    target_sdk: None,
  }
}

///|
fn default_ios_config() -> IosConfig {
  { sdk: None, arch: None, deployment_target: None, }
}

///|
fn default_paths_config() -> PathsConfig {
  { moon_home: None, moui_root: None, skia_root: None, }
}

///|
fn default_cli_config() -> CliConfig {
  {
    schema_version: 1,
    harmonyos: default_harmonyos_config(),
    android: default_android_config(),
    ios: default_ios_config(),
    paths: default_paths_config(),
  }
}

///|
let cli_config_schema_version : Int = 1

///|
fn validate_cli_config(config : CliConfig) -> Result[Unit, String] {
  if config.schema_version != cli_config_schema_version {
    return Err(
      "cli config schemaVersion must be " +
      cli_config_schema_version.to_string() +
      ", got " +
      config.schema_version.to_string(),
    )
  }
  Ok(())
}

///|
fn harmonyos_config_to_json(config : HarmonyosConfig) -> Json {
  let fields : Map[String, Json] = Map([])
  match config.sdk_home {
    Some(value) => fields["sdkHome"] = Json::string(value)
    None => ()
  }
  match config.hvigorw {
    Some(value) => fields["hvigorw"] = Json::string(value)
    None => ()
  }
  match config.ohpm {
    Some(value) => fields["ohpm"] = Json::string(value)
    None => ()
  }
  match config.signing_config_file {
    Some(value) => fields["signingConfigFile"] = Json::string(value)
    None => ()
  }
  Json::object(fields)
}

///|
fn android_config_to_json(config : AndroidConfig) -> Json {
  let fields : Map[String, Json] = Map([])
  match config.sdk_home {
    Some(value) => fields["sdkHome"] = Json::string(value)
    None => ()
  }
  match config.ndk_home {
    Some(value) => fields["ndkHome"] = Json::string(value)
    None => ()
  }
  let abi : Array[Json] = []
  for value in config.abi {
    abi.push(Json::string(value))
  }
  fields["abi"] = Json::array(abi)
  match config.compile_sdk {
    Some(value) => fields["compileSdk"] = Json::number(value.to_double())
    None => ()
  }
  match config.target_sdk {
    Some(value) => fields["targetSdk"] = Json::number(value.to_double())
    None => ()
  }
  Json::object(fields)
}

///|
fn ios_config_to_json(config : IosConfig) -> Json {
  let fields : Map[String, Json] = Map([])
  match config.sdk {
    Some(value) => fields["sdk"] = Json::string(value)
    None => ()
  }
  match config.arch {
    Some(value) => fields["arch"] = Json::string(value)
    None => ()
  }
  match config.deployment_target {
    Some(value) => fields["deploymentTarget"] = Json::number(value.to_double())
    None => ()
  }
  Json::object(fields)
}

///|
fn paths_config_to_json(config : PathsConfig) -> Json {
  let fields : Map[String, Json] = Map([])
  match config.moon_home {
    Some(value) => fields["moonHome"] = Json::string(value)
    None => ()
  }
  match config.moui_root {
    Some(value) => fields["mouiRoot"] = Json::string(value)
    None => ()
  }
  match config.skia_root {
    Some(value) => fields["skiaRoot"] = Json::string(value)
    None => ()
  }
  Json::object(fields)
}

///|
fn cli_config_to_json(config : CliConfig) -> Json {
  let fields : Map[String, Json] = Map([])
  fields["schemaVersion"] = Json::number(config.schema_version.to_double())
  fields["harmonyos"] = harmonyos_config_to_json(config.harmonyos)
  fields["android"] = android_config_to_json(config.android)
  fields["ios"] = ios_config_to_json(config.ios)
  fields["paths"] = paths_config_to_json(config.paths)
  Json::object(fields)
}

///|
fn optional_string(
  fields : Map[String, Json],
  key : String,
) -> Result[String?, String] {
  match fields.get(key) {
    None => Ok(None)
    Some(Null) => Ok(None)
    Some(String(value)) => Ok(Some(value))
    Some(_) => Err("config field " + key + " must be a string or null")
  }
}

///|
fn optional_int(
  fields : Map[String, Json],
  key : String,
) -> Result[Int?, String] {
  match fields.get(key) {
    None => Ok(None)
    Some(Null) => Ok(None)
    Some(Number(value, repr=_)) if value.to_int().to_double() == value =>
      Ok(Some(value.to_int()))
    Some(_) => Err("config field " + key + " must be an integer or null")
  }
}

///|
fn required_string_array(
  fields : Map[String, Json],
  key : String,
) -> Result[Array[String], String] {
  match fields.get(key) {
    None => Ok([])
    Some(Null) => Ok([])
    Some(Array(values)) => {
      let result : Array[String] = []
      for value in values {
        guard value is String(text) else {
          return Err("config field " + key + " must be an array of strings")
        }
        result.push(text)
      }
      Ok(result)
    }
    Some(_) => Err("config field " + key + " must be an array")
  }
}

///|
fn harmonyos_config_from_json(value : Json) -> Result[HarmonyosConfig, String] {
  guard value is Object(fields) else {
    return Err("cli config harmonyos must be an object")
  }
  let sdk_home = match optional_string(fields, "sdkHome") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let hvigorw = match optional_string(fields, "hvigorw") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let ohpm = match optional_string(fields, "ohpm") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let signing_config_file = match optional_string(fields, "signingConfigFile") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok({ sdk_home, hvigorw, ohpm, signing_config_file, })
}

///|
fn android_config_from_json(value : Json) -> Result[AndroidConfig, String] {
  guard value is Object(fields) else {
    return Err("cli config android must be an object")
  }
  let sdk_home = match optional_string(fields, "sdkHome") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let ndk_home = match optional_string(fields, "ndkHome") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let abi = match required_string_array(fields, "abi") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let compile_sdk = match optional_int(fields, "compileSdk") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let target_sdk = match optional_int(fields, "targetSdk") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok({ sdk_home, ndk_home, abi, compile_sdk, target_sdk, })
}

///|
fn ios_config_from_json(value : Json) -> Result[IosConfig, String] {
  guard value is Object(fields) else {
    return Err("cli config ios must be an object")
  }
  let sdk = match optional_string(fields, "sdk") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let arch = match optional_string(fields, "arch") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let deployment_target = match optional_int(fields, "deploymentTarget") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok({ sdk, arch, deployment_target, })
}

///|
fn paths_config_from_json(value : Json) -> Result[PathsConfig, String] {
  guard value is Object(fields) else {
    return Err("cli config paths must be an object")
  }
  let moon_home = match optional_string(fields, "moonHome") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let moui_root = match optional_string(fields, "mouiRoot") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let skia_root = match optional_string(fields, "skiaRoot") {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok({ moon_home, moui_root, skia_root, })
}

///|
fn cli_config_from_json(value : Json) -> Result[CliConfig, String] {
  guard value is Object(fields) else {
    return Err("cli config must be an object")
  }
  let schema_version = match fields.get("schemaVersion") {
    Some(Number(value, repr=_)) if value.to_int().to_double() == value =>
      value.to_int()
    _ => return Err("cli config schemaVersion must be an integer")
  }
  let harmonyos = match fields.get("harmonyos") {
    None => default_harmonyos_config()
    Some(value) =>
      match harmonyos_config_from_json(value) {
        Ok(v) => v
        Err(e) => return Err(e)
      }
  }
  let android = match fields.get("android") {
    None => default_android_config()
    Some(value) =>
      match android_config_from_json(value) {
        Ok(v) => v
        Err(e) => return Err(e)
      }
  }
  let ios = match fields.get("ios") {
    None => default_ios_config()
    Some(value) =>
      match ios_config_from_json(value) {
        Ok(v) => v
        Err(e) => return Err(e)
      }
  }
  let paths = match fields.get("paths") {
    None => default_paths_config()
    Some(value) =>
      match paths_config_from_json(value) {
        Ok(v) => v
        Err(e) => return Err(e)
      }
  }
  let config : CliConfig = { schema_version, harmonyos, android, ios, paths, }
  match validate_cli_config(config) {
    Ok(_) => Ok(config)
    Err(message) => Err(message)
  }
}