///| Counts keys by section in a merged view.
pub struct SectionStats {
  section : String
  key_count : Int
  sensitive_count : Int
} derive(@debug.Debug, Eq)

///| Describes how one final value was produced.
pub struct ValueTrace {
  path : String
  final_layer : String
  final_source : String
  override_count : Int
  sensitive : Bool
} derive(@debug.Debug, Eq)

///| Aggregate view statistics.
pub struct ViewStats {
  key_count : Int
  section_count : Int
  sensitive_count : Int
  overridden_count : Int
  diagnostics_count : Int
} derive(@debug.Debug, Eq)

pub fn analyze_view(view : ConfigView) -> ViewStats {
  {
    key_count: view.values.length(),
    section_count: section_stats(view).length(),
    sensitive_count: sensitive_paths(view).length(),
    overridden_count: overridden_values(view).length(),
    diagnostics_count: view.diagnostics.length(),
  }
}

pub fn section_stats(view : ConfigView) -> Array[SectionStats] {
  let sections = collect_analysis_sections(view)
  let stats : Array[SectionStats] = []
  for section_name in sections {
    let mut key_count = 0
    let mut sensitive_count = 0
    for item in view.values {
      if normalized_section(item.section) == section_name {
        key_count = key_count + 1
        if is_sensitive_path(entry_path(item.section, item.key)) {
          sensitive_count = sensitive_count + 1
        }
      }
    }
    stats.push({ section: section_name, key_count, sensitive_count })
  }
  stats
}

fn collect_analysis_sections(view : ConfigView) -> Array[String] {
  let sections : Array[String] = []
  for item in view.values {
    let name = normalized_section(item.section)
    if !analysis_contains(sections, name) {
      sections.push(name)
    }
  }
  sections
}

fn normalized_section(section : String) -> String {
  if section == "" { "" } else { section }
}

fn analysis_contains(values : Array[String], target : String) -> Bool {
  for value in values {
    if value == target {
      return true
    }
  }
  false
}

pub fn sensitive_paths(view : ConfigView) -> Array[String] {
  let paths : Array[String] = []
  for item in view.values {
    let path = entry_path(item.section, item.key)
    if is_sensitive_path(path) {
      paths.push(path)
    }
  }
  paths
}

pub fn overridden_values(view : ConfigView) -> Array[ResolvedValue] {
  let values : Array[ResolvedValue] = []
  for item in view.values {
    if item.overwritten_by.length() > 0 {
      values.push(item)
    }
  }
  values
}

pub fn trace_values(view : ConfigView) -> Array[ValueTrace] {
  let traces : Array[ValueTrace] = []
  for item in view.values {
    let path = entry_path(item.section, item.key)
    traces.push({
      path,
      final_layer: item.source_layer,
      final_source: item.source + ":" + item.span.line.to_string(),
      override_count: item.overwritten_by.length(),
      sensitive: is_sensitive_path(path),
    })
  }
  traces
}

pub fn render_stats_markdown(view : ConfigView) -> String {
  let stats = analyze_view(view)
  let builder = StringBuilder()
  builder.write_string("# Configuration Statistics\n\n")
  builder.write_string("- Keys: " + stats.key_count.to_string() + "\n")
  builder.write_string("- Sections: " + stats.section_count.to_string() + "\n")
  builder.write_string("- Sensitive keys: " + stats.sensitive_count.to_string() + "\n")
  builder.write_string("- Overridden keys: " + stats.overridden_count.to_string() + "\n")
  builder.write_string("- Diagnostics: " + stats.diagnostics_count.to_string() + "\n\n")
  builder.write_string("## Sections\n\n")
  builder.write_string("| Section | Keys | Sensitive |\n")
  builder.write_string("| --- | --- | --- |\n")
  for item in section_stats(view) {
    builder.write_string("| " + item.section + " | " + item.key_count.to_string() + " | " + item.sensitive_count.to_string() + " |\n")
  }
  builder.to_string()
}

pub fn render_trace_markdown(view : ConfigView) -> String {
  let builder = StringBuilder()
  builder.write_string("| Path | Final Layer | Source | Overrides | Sensitive |\n")
  builder.write_string("| --- | --- | --- | --- | --- |\n")
  for item in trace_values(view) {
    builder.write_string("| " + item.path + " | " + item.final_layer + " | " + item.final_source + " | " + item.override_count.to_string() + " | " + yes_no(item.sensitive) + " |\n")
  }
  builder.to_string()
}

fn yes_no(value : Bool) -> String {
  if value { "yes" } else { "no" }
}

///| Measures how many schema fields are filled by current values or defaults.
pub fn schema_coverage(schema : ConfigSchema, view : ConfigView) -> Int {
  let mut total = 0
  let mut covered = 0
  for schema_section in schema.sections {
    for schema_field in schema_section.fields {
      total = total + 1
      if view.get_path(schema_field.path) != None || schema_field.default_value != None {
        covered = covered + 1
      }
    }
  }
  if total == 0 {
    100
  } else {
    covered * 100 / total
  }
}

pub fn missing_schema_fields(schema : ConfigSchema, view : ConfigView) -> Array[String] {
  let missing : Array[String] = []
  for schema_section in schema.sections {
    for schema_field in schema_section.fields {
      if view.get_path(schema_field.path) == None && schema_field.default_value == None {
        missing.push(schema_field.path)
      }
    }
  }
  missing
}

pub fn render_schema_gap_markdown(schema : ConfigSchema, view : ConfigView) -> String {
  let builder = StringBuilder()
  builder.write_string("# Schema Gap Report\n\n")
  builder.write_string("- Schema: " + schema.name + "\n")
  builder.write_string("- Coverage: " + schema_coverage(schema, view).to_string() + "%\n\n")
  let missing = missing_schema_fields(schema, view)
  if missing.length() == 0 {
    builder.write_string("All schema fields are present or have defaults.\n")
  } else {
    builder.write_string("## Missing Fields\n\n")
    for path in missing {
      builder.write_string("- " + path + "\n")
    }
  }
  builder.to_string()
}