///|
pub(all) enum OutputFormat {
  Css
  CssMinified
  Json
  JsonLines
  MoonBit
  Markdown
} derive(Debug, Eq)

///|
pub fn OutputFormat::label(self : OutputFormat) -> String {
  match self {
    Css => "css"
    CssMinified => "css-minified"
    Json => "json"
    JsonLines => "json-lines"
    MoonBit => "moonbit"
    Markdown => "markdown"
  }
}

///|
pub(all) struct BuildOptions {
  canonicalize : Bool
  resolve_aliases : Bool
  output_format : OutputFormat
} derive(Debug, Eq)

///|
pub fn BuildOptions::default() -> BuildOptions {
  { canonicalize: true, resolve_aliases: false, output_format: Css }
}

///|
pub fn BuildOptions::with_format(
  self : BuildOptions,
  output_format : OutputFormat,
) -> BuildOptions {
  { ..self, output_format, }
}

///|
pub fn BuildOptions::with_alias_resolution(
  self : BuildOptions,
  enabled : Bool,
) -> BuildOptions {
  { ..self, resolve_aliases: enabled }
}

///|
pub fn BuildOptions::without_canonicalization(
  self : BuildOptions,
) -> BuildOptions {
  { ..self, canonicalize: false }
}

///|
pub struct BuildResult {
  tokens : Array[Token]
  parse_problems : Array[ParseProblem]
  token_problems : Array[TokenProblem]
  alias_problems : Array[String]
  output : String
  format : OutputFormat
} derive(Debug, Eq)

///|
pub fn BuildResult::tokens(self : BuildResult) -> Array[Token] {
  self.tokens
}

///|
pub fn BuildResult::parse_problems(self : BuildResult) -> Array[ParseProblem] {
  self.parse_problems
}

///|
pub fn BuildResult::token_problems(self : BuildResult) -> Array[TokenProblem] {
  self.token_problems
}

///|
pub fn BuildResult::alias_problems(self : BuildResult) -> Array[String] {
  self.alias_problems
}

///|
pub fn BuildResult::output(self : BuildResult) -> String {
  self.output
}

///|
pub fn BuildResult::format(self : BuildResult) -> OutputFormat {
  self.format
}

///|
pub fn BuildResult::ok(self : BuildResult) -> Bool {
  self.parse_problems.length() == 0 &&
  self.token_problems.length() == 0 &&
  self.alias_problems.length() == 0
}

///|
pub fn BuildResult::summary(self : BuildResult) -> String {
  "tokens=\{self.tokens.length()}, parse=\{self.parse_problems.length()}, token=\{self.token_problems.length()}, aliases=\{self.alias_problems.length()}, format=\{self.format.label()}"
}

///|
pub fn BuildResult::diagnostics(self : BuildResult) -> String {
  let lines : Array[String] = []
  for problem in self.parse_problems {
    lines.push("parse: \{problem.message()}")
  }
  for problem in self.token_problems {
    lines.push("token: \{problem.message()}")
  }
  for problem in self.alias_problems {
    lines.push("alias: \{problem}")
  }
  lines.join("\n")
}

///|
pub fn build_tokens(input : String, options~ : BuildOptions) -> BuildResult {
  let parsed = parse_tokens(input)
  let parsed_tokens = if options.canonicalize {
    canonical_tokens(parsed.tokens)
  } else {
    parsed.tokens
  }
  let aliases = alias_problems(parsed_tokens)
  let output_tokens = if options.resolve_aliases {
    resolve_aliases(parsed_tokens)
  } else {
    parsed_tokens
  }
  let output = render_output(output_tokens, options.output_format)
  {
    tokens: output_tokens,
    parse_problems: parsed.problems,
    token_problems: validate_all(output_tokens),
    alias_problems: aliases,
    output,
    format: options.output_format,
  }
}

///|
pub fn build_text(input : String, format~ : OutputFormat) -> BuildResult {
  build_tokens(input, options=BuildOptions::default().with_format(format))
}

///|
pub fn render_output(tokens : Array[Token], format : OutputFormat) -> String {
  match format {
    Css => to_css(tokens)
    CssMinified => to_css_minified(tokens)
    Json => to_json(tokens)
    JsonLines => to_json_lines(tokens)
    MoonBit => to_moonbit_constants(tokens)
    Markdown => to_markdown_sorted(tokens)
  }
}

///|
fn alias_problems(tokens : Array[Token]) -> Array[String] {
  let problems : Array[String] = []
  for problem in unresolved_aliases(tokens) {
    problems.push(problem)
  }
  for problem in alias_cycles(tokens) {
    problems.push(problem)
  }
  problems
}