///|
pub(all) enum PropertyKind {
  Choice(Array[String])
  Boolean
  PositiveInteger
  PositiveIntegerOrTab
  FreeText
} derive(Debug, Eq)

///|
pub(all) struct PropertySchema {
  name : String
  kind : PropertyKind
  description : String
  standard : Bool
} derive(Debug, Eq)

///|
pub fn standard_property_schemas() -> Array[PropertySchema] {
  [
    {
      name: "indent_style",
      kind: Choice(["tab", "space"]),
      description: "缩进使用制表符或空格",
      standard: true,
    },
    {
      name: "indent_size",
      kind: PositiveIntegerOrTab,
      description: "每级缩进的列数",
      standard: true,
    },
    {
      name: "tab_width",
      kind: PositiveInteger,
      description: "制表符显示宽度",
      standard: true,
    },
    {
      name: "end_of_line",
      kind: Choice(["lf", "crlf", "cr"]),
      description: "换行符格式",
      standard: true,
    },
    {
      name: "charset",
      kind: Choice(["latin1", "utf-8", "utf-8-bom", "utf-16be", "utf-16le"]),
      description: "文件字符编码",
      standard: true,
    },
    {
      name: "trim_trailing_whitespace",
      kind: Boolean,
      description: "是否移除行尾空白",
      standard: true,
    },
    {
      name: "insert_final_newline",
      kind: Boolean,
      description: "是否保证文件末尾换行",
      standard: true,
    },
    {
      name: "max_line_length",
      kind: PositiveInteger,
      description: "建议的最大行宽",
      standard: true,
    },
    {
      name: "root",
      kind: Boolean,
      description: "是否停止搜索父目录",
      standard: true,
    },
  ]
}

///|
pub fn property_schema(name : String) -> PropertySchema? {
  let normalized = lower_ascii(name)
  for schema in standard_property_schemas() {
    if schema.name == normalized {
      return Some(schema)
    }
  }
  None
}

///|
fn is_positive_integer(value : String) -> Bool {
  guard parse_decimal(value) is Some(number) else { return false }
  number > 0
}

///|
fn valid_for_kind(value : String, kind : PropertyKind) -> Bool {
  if lower_ascii(value) == "unset" {
    return true
  }
  match kind {
    Choice(values) => values.any(candidate => candidate == lower_ascii(value))
    Boolean => lower_ascii(value) == "true" || lower_ascii(value) == "false"
    PositiveInteger => is_positive_integer(value)
    PositiveIntegerOrTab =>
      lower_ascii(value) == "tab" || is_positive_integer(value)
    FreeText => value != ""
  }
}

///|
fn expected_description(kind : PropertyKind) -> String {
  match kind {
    Choice(values) => "可选值:" + values.join("、") + ",或 unset"
    Boolean => "可选值:true、false 或 unset"
    PositiveInteger => "应为正整数或 unset"
    PositiveIntegerOrTab => "应为正整数、tab 或 unset"
    FreeText => "值不能为空"
  }
}

///|
fn validate_property(
  property : Property,
  diagnostics : Array[Diagnostic],
) -> Unit {
  match property_schema(property.key) {
    None => ()
    Some(schema) =>
      if !valid_for_kind(property.value, schema.kind) {
        diagnostics.push({
          line: property.source.line,
          severity: Error,
          code: "EC2001",
          message: "属性 `" +
          property.key +
          "` 的值 `" +
          property.value +
          "` 无效",
          hint: expected_description(schema.kind),
          span: property.span,
        })
      }
  }
}

///|
fn validate_cross_properties(
  section : Section,
  diagnostics : Array[Diagnostic],
) -> Unit {
  let mut indent_style : Property? = None
  let mut indent_size : Property? = None
  let mut tab_width : Property? = None
  for property in section.properties {
    match property.key {
      "indent_style" => indent_style = Some(property)
      "indent_size" => indent_size = Some(property)
      "tab_width" => tab_width = Some(property)
      _ => ()
    }
  }
  if indent_style is Some(style) &&
    lower_ascii(style.value) == "tab" &&
    indent_size is None &&
    tab_width is Some(width) {
    diagnostics.push({
      line: width.source.line,
      severity: Information,
      code: "EC2101",
      message: "indent_size 未设置,将继承 tab_width = " + width.value,
      hint: "这是 EditorConfig 的标准派生行为。",
      span: width.span,
    })
  }
}

///|
pub fn validate(config : EditorConfig) -> Array[Diagnostic] {
  let diagnostics : Array[Diagnostic] = []
  for property in config.preamble {
    validate_property(property, diagnostics)
  }
  for section in config.sections {
    let program = compile_glob(section.pattern)
    for issue in program.diagnostics {
      diagnostics.push({
        line: section.source.line,
        severity: Error,
        code: "EC2002",
        message: "无效的 Glob:" + issue,
        hint: "检查字符集合和转义符是否闭合。",
        span: section.span,
      })
    }
    for property in section.properties {
      validate_property(property, diagnostics)
    }
    validate_cross_properties(section, diagnostics)
  }
  diagnostics
}

///|
pub fn has_errors(diagnostics : Array[Diagnostic]) -> Bool {
  diagnostics.any(diagnostic => diagnostic.severity is Error)
}