// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2026 clbbbb

///|
pub fn parse_policy_config(text : String) -> Policy {
  let mut allowed : Array[String] = []
  let mut denied : Array[String] = []
  let mut allow_copyleft = false
  let mut require_osi = true
  for line in split_char(text, '\n') {
    let clean = strip_config_comment(line)
    match config_pair(clean) {
      Some((key, value)) =>
        if key == "allow" || key == "allowed" {
          allowed = normalize_license_list(split_config_list(value))
        } else if key == "deny" || key == "denied" {
          denied = normalize_license_list(split_config_list(value))
        } else if key == "allow_copyleft" {
          allow_copyleft = parse_bool(value)
        } else if key == "require_osi" {
          require_osi = parse_bool(value)
        }
      None => ()
    }
  }
  { allowed, denied, allow_copyleft, require_osi }
}

///|
pub fn policy_config_errors(text : String) -> Array[String] {
  let errors : Array[String] = []
  let lines = split_char(text, '\n')
  for index = 0; index < lines.length(); index = index + 1 {
    let clean = strip_config_comment(lines[index])
    if clean != "" {
      match config_pair(clean) {
        None =>
          errors.push(
            "line " + (index + 1).to_string() + ": expected key = value",
          )
        Some((key, value)) =>
          if !is_policy_config_key(key) {
            errors.push(
              "line " + (index + 1).to_string() + ": unknown key " + key,
            )
          } else if (key == "allow_copyleft" || key == "require_osi") &&
            !is_bool_config_value(value) {
            errors.push(
              "line " +
              (index + 1).to_string() +
              ": " +
              key +
              " must be a boolean",
            )
          } else if key == "allow" ||
            key == "allowed" ||
            key == "deny" ||
            key == "denied" {
            for item in split_config_list(value) {
              for error in validation_errors(item) {
                errors.push("line " + (index + 1).to_string() + ": " + error)
              }
            }
          }
      }
    }
  }
  errors
}

///|
pub fn is_policy_config_key(key : String) -> Bool {
  key == "allow" ||
  key == "allowed" ||
  key == "deny" ||
  key == "denied" ||
  key == "allow_copyleft" ||
  key == "require_osi"
}

///|
pub fn policy_config_valid(text : String) -> Bool {
  policy_config_errors(text).is_empty()
}

///|
pub fn strip_config_comment(line : String) -> String {
  match line.find("#") {
    Some(index) => trim_ascii(line.unsafe_substring(start=0, end=index))
    None => trim_ascii(line)
  }
}

///|
pub fn config_pair(line : String) -> (String, String)? {
  match line.find("=") {
    Some(index) => {
      let key = lower_ascii(
        trim_ascii(line.unsafe_substring(start=0, end=index)),
      )
      let value = trim_ascii(
        line.unsafe_substring(start=index + 1, end=line.length()),
      )
      if key == "" {
        None
      } else {
        Some((key, value))
      }
    }
    None => None
  }
}

///|
pub fn split_config_list(text : String) -> Array[String] {
  let rows : Array[String] = []
  for item in split_char(text, ',') {
    let clean = trim_ascii(item)
    if clean != "" {
      rows.push(clean)
    }
  }
  rows
}

///|
pub fn parse_bool(text : String) -> Bool {
  let value = lower_ascii(trim_ascii(text))
  value == "true" || value == "yes" || value == "1" || value == "on"
}

///|
pub fn policy_config_report(policy : Policy) -> String {
  join_lines([
    "allowed=" + join_with(policy.allowed, ","),
    "denied=" + join_with(policy.denied, ","),
    "allow_copyleft=" + bool_word(policy.allow_copyleft),
    "require_osi=" + bool_word(policy.require_osi),
  ])
}

///|
pub fn merge_policy(base : Policy, extra : Policy) -> Policy {
  {
    allowed: if extra.allowed.is_empty() {
      base.allowed
    } else {
      extra.allowed
    },
    denied: unique_strings(base.denied + extra.denied),
    allow_copyleft: base.allow_copyleft || extra.allow_copyleft,
    require_osi: base.require_osi && extra.require_osi,
  }
}

///|
pub fn policy_from_mode(mode : String) -> Policy {
  let value = lower_ascii(trim_ascii(mode))
  if value == "permissive" {
    permissive_policy()
  } else if value == "copyleft-ok" {
    { allowed: [], denied: [], allow_copyleft: true, require_osi: true }
  } else if value == "any-known" {
    { allowed: [], denied: [], allow_copyleft: true, require_osi: false }
  } else {
    permissive_policy()
  }
}