///|
/// TOML Value - represents any TOML value
pub(all) enum TomlValue {
  String(String)
  Integer(Int64)
  Float(Double)
  Boolean(Bool)
  DateTime(String) // Simplified: store as string
  Array(Array[TomlValue])
  Table(Map[String, TomlValue])
} derive(Show, Eq)

///|
/// Token types for the lexer
enum Token {
  // Literals
  TokString(String)
  TokInteger(Int64)
  TokFloat(Double)
  TokBool(Bool)
  TokDateTime(String)

  // Symbols
  TokEqual // =
  TokDot // .
  TokComma // ,
  TokLeftBracket // [
  TokRightBracket // ]
  TokLeftBrace // {
  TokRightBrace // }
  TokNewline // \n

  // Identifiers
  TokIdent(String)

  // Special
  TokEOF
} derive(Show, Eq)

///|
/// Lexer state
struct Lexer {
  input : String
  mut pos : Int
  mut line : Int
  mut col : Int
}

///|
/// Create a new lexer
fn Lexer::new(input : String) -> Lexer {
  { input, pos: 0, line: 1, col: 1 }
}

///|
/// Peek at current character without consuming
fn Lexer::peek(self : Lexer) -> Char? {
  if self.pos >= self.input.length() {
    None
  } else {
    self.input.get_char(self.pos)
  }
}

///|
/// Consume and return current character
fn Lexer::next(self : Lexer) -> Char? {
  match self.peek() {
    Some(ch) => {
      self.pos += 1
      if ch == '\n' {
        self.line += 1
        self.col = 1
      } else {
        self.col += 1
      }
      Some(ch)
    }
    None => None
  }
}

///|
/// Skip whitespace (except newlines)
fn Lexer::skip_whitespace(self : Lexer) -> Unit {
  while true {
    match self.peek() {
      Some(' ') | Some('\t') | Some('\r') => {
        self.next() |> ignore
        continue
      }
      _ => break
    }
  }
}

///|
/// Skip a comment (from # to end of line)
fn Lexer::skip_comment(self : Lexer) -> Unit {
  while true {
    match self.peek() {
      Some('\n') | None => break
      _ => {
        self.next() |> ignore
        continue
      }
    }
  }
}

///|
/// Check if a character is a valid identifier start
fn is_ident_start(ch : Char) -> Bool {
  (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '-'
}

///|
/// Check if a character is a valid identifier continuation
fn is_ident_continue(ch : Char) -> Bool {
  (ch >= 'a' && ch <= 'z') ||
  (ch >= 'A' && ch <= 'Z') ||
  (ch >= '0' && ch <= '9') ||
  ch == '_' ||
  ch == '-'
}

///|
/// Lex an identifier or boolean
fn Lexer::lex_ident(self : Lexer) -> Token {
  let start = self.pos
  while true {
    match self.peek() {
      Some(ch) if is_ident_continue(ch) => {
        self.next() |> ignore
        continue
      }
      _ => break
    }
  }
  // Build string from characters to avoid StringView::to_string() which may raise
  let chars : Array[Char] = []
  for i = start; i < self.pos; i = i + 1 {
    match self.input.get_char(i) {
      Some(ch) => chars.push(ch)
      None => break
    }
  }
  let text = String::from_array(chars)
  match text {
    "true" => TokBool(true)
    "false" => TokBool(false)
    _ => TokIdent(text)
  }
}

///|
/// Lex a string (basic or literal)
fn Lexer::lex_string(self : Lexer) -> Token raise {
  self.next() |> ignore // consume opening quote
  let chars : Array[Char] = []
  while true {
    match self.next() {
      Some('"') => return TokString(String::from_array(chars))
      Some('\\') => {
        match self.next() {
          Some('n') => chars.push('\n')
          Some('t') => chars.push('\t')
          Some('r') => chars.push('\r')
          Some('\\') => chars.push('\\')
          Some('"') => chars.push('"')
          Some(ch) => chars.push(ch)
          None => raise Failure("Unexpected end of string")
        }
        continue
      }
      Some(ch) => {
        chars.push(ch)
        continue
      }
      None => raise Failure("Unterminated string")
    }
  } else {
    // Unreachable - while true always continues or returns
    raise Failure("Unreachable")
  }
}

///|
/// Lex a number (integer or float)
fn Lexer::lex_number(self : Lexer) -> Token raise {
  let start = self.pos
  let mut is_float = false

  // Handle optional sign
  match self.peek() {
    Some('+') | Some('-') => self.next() |> ignore
    _ => ()
  }

  // Collect digits
  while true {
    match self.peek() {
      Some(ch) if ch >= '0' && ch <= '9' => {
        self.next() |> ignore
        continue
      }
      Some('_') => {
        self.next() |> ignore // Skip underscores in numbers
        continue
      }
      Some('.') => {
        is_float = true
        self.next() |> ignore
        continue
      }
      Some('e') | Some('E') => {
        is_float = true
        self.next() |> ignore
        match self.peek() {
          Some('+') | Some('-') => self.next() |> ignore
          _ => ()
        }
        continue
      }
      _ => break
    }
  }

  // Build string from characters, skipping underscores
  let chars : Array[Char] = []
  for i = start; i < self.pos; i = i + 1 {
    match self.input.get_char(i) {
      Some('_') => continue
      Some(ch) => chars.push(ch)
      None => break
    }
  }
  let text = String::from_array(chars)
  if is_float {
    try {
      let f = @strconv.parse_double(text)
      TokFloat(f)
    } catch {
      _ => raise Failure("Invalid float: \{text}")
    }
  } else {
    try {
      let i = @strconv.parse_int64(text)
      TokInteger(i)
    } catch {
      _ => raise Failure("Invalid integer: \{text}")
    }
  }
}

///|
/// Get the next token
fn Lexer::next_token(self : Lexer) -> Token raise {
  while true {
    self.skip_whitespace()
    match self.peek() {
      None => return TokEOF
      Some('#') => {
        self.skip_comment()
        continue
      }
      Some('\n') => {
        self.next() |> ignore
        return TokNewline
      }
      Some('=') => {
        self.next() |> ignore
        return TokEqual
      }
      Some('.') => {
        self.next() |> ignore
        return TokDot
      }
      Some(',') => {
        self.next() |> ignore
        return TokComma
      }
      Some('[') => {
        self.next() |> ignore
        return TokLeftBracket
      }
      Some(']') => {
        self.next() |> ignore
        return TokRightBracket
      }
      Some('{') => {
        self.next() |> ignore
        return TokLeftBrace
      }
      Some('}') => {
        self.next() |> ignore
        return TokRightBrace
      }
      Some('"') => return self.lex_string()
      Some(ch) if (ch >= '0' && ch <= '9') || ch == '+' || ch == '-' =>
        return self.lex_number()
      Some(ch) if is_ident_start(ch) => return self.lex_ident()
      Some(ch) => raise Failure("Unexpected character: \{ch}")
    }
  } else {
    // Unreachable
    raise Failure("Unreachable")
  }
}

///|
/// Tokenize entire input
fn tokenize(input : String) -> Array[Token] raise {
  let lexer = Lexer::new(input)
  let tokens : Array[Token] = []
  while true {
    let tok = lexer.next_token()
    tokens.push(tok)
    if tok == TokEOF {
      return tokens
    }
    continue
  } else {
    // Unreachable
    raise Failure("Unreachable")
  }
}

///|
/// Parser state
struct Parser {
  tokens : Array[Token]
  mut pos : Int
}

///|
/// Create a new parser
fn Parser::new(tokens : Array[Token]) -> Parser {
  { tokens, pos: 0 }
}

///|
/// Peek at current token
fn Parser::peek(self : Parser) -> Token {
  if self.pos >= self.tokens.length() {
    TokEOF
  } else {
    self.tokens[self.pos]
  }
}

///|
/// Consume and return current token
fn Parser::next(self : Parser) -> Token {
  let tok = self.peek()
  if self.pos < self.tokens.length() {
    self.pos += 1
  }
  tok
}

///|
/// Skip newline tokens
fn Parser::skip_newlines(self : Parser) -> Unit {
  while true {
    match self.peek() {
      TokNewline => {
        self.next() |> ignore
        continue
      }
      _ => break
    }
  }
}

///|
/// Expect a specific token
fn Parser::expect(self : Parser, expected : Token) -> Unit raise {
  let tok = self.next()
  if tok != expected {
    raise Failure("Expected \{expected}, got \{tok}")
  }
}

///|
/// Parse a value
fn Parser::parse_value(self : Parser) -> TomlValue raise {
  match self.peek() {
    TokString(s) => {
      self.next() |> ignore
      String(s)
    }
    TokInteger(i) => {
      self.next() |> ignore
      Integer(i)
    }
    TokFloat(f) => {
      self.next() |> ignore
      Float(f)
    }
    TokBool(b) => {
      self.next() |> ignore
      Boolean(b)
    }
    TokDateTime(dt) => {
      self.next() |> ignore
      DateTime(dt)
    }
    TokLeftBracket => self.parse_array()
    TokLeftBrace => self.parse_inline_table()
    tok => raise Failure("Unexpected token in value: \{tok}")
  }
}

///|
/// Parse an array
fn Parser::parse_array(self : Parser) -> TomlValue raise {
  self.expect(TokLeftBracket)
  self.skip_newlines()
  let items : Array[TomlValue] = []
  while true {
    match self.peek() {
      TokRightBracket => {
        self.next() |> ignore
        return Array(items)
      }
      _ => {
        items.push(self.parse_value())
        self.skip_newlines()
        match self.peek() {
          TokComma => {
            self.next() |> ignore
            self.skip_newlines()
            continue
          }
          TokRightBracket => continue
          tok => raise Failure("Expected comma or ] in array, got \{tok}")
        }
      }
    }
  } else {
    raise Failure("Unreachable")
  }
}

///|
/// Parse an inline table
fn Parser::parse_inline_table(self : Parser) -> TomlValue raise {
  self.expect(TokLeftBrace)
  let table : Map[String, TomlValue] = {}
  while true {
    match self.peek() {
      TokRightBrace => {
        self.next() |> ignore
        return Table(table)
      }
      TokIdent(key) => {
        self.next() |> ignore
        self.expect(TokEqual)
        let value = self.parse_value()
        table[key] = value
        match self.peek() {
          TokComma => {
            self.next() |> ignore
            continue
          }
          TokRightBrace => continue
          tok =>
            raise Failure("Expected comma or }} in inline table, got \{tok}")
        }
      }
      tok => raise Failure("Expected key in inline table, got \{tok}")
    }
  } else {
    raise Failure("Unreachable")
  }
}

///|
/// Parse a key path (e.g., "a.b.c")
fn Parser::parse_key_path(self : Parser) -> Array[String] raise {
  let keys : Array[String] = []
  while true {
    match self.peek() {
      TokIdent(key) => {
        keys.push(key)
        self.next() |> ignore
        match self.peek() {
          TokDot => {
            self.next() |> ignore
            continue
          }
          _ => return keys
        }
      }
      tok => raise Failure("Expected identifier in key path, got \{tok}")
    }
  } else {
    raise Failure("Unreachable")
  }
}

///|
/// Set a nested value in a table
fn set_nested(
  table : Map[String, TomlValue],
  keys : Array[String],
  value : TomlValue,
) -> Unit raise {
  if keys.length() == 0 {
    raise Failure("Empty key path")
  }
  if keys.length() == 1 {
    table[keys[0]] = value
    return
  }
  let first_key = keys[0]
  let rest_keys = keys[1:].to_array()
  match table.get(first_key) {
    Some(Table(nested)) => set_nested(nested, rest_keys, value)
    Some(_) =>
      raise Failure("Key already exists with non-table value: \{first_key}")
    None => {
      let nested : Map[String, TomlValue] = {}
      table[first_key] = Table(nested)
      set_nested(nested, rest_keys, value)
    }
  }
}

///|
/// Get or create a nested table
fn get_or_create_table(
  table : Map[String, TomlValue],
  keys : Array[String],
) -> Map[String, TomlValue] raise {
  if keys.length() == 0 {
    return table
  }
  let first_key = keys[0]
  let rest_keys = keys[1:].to_array()
  match table.get(first_key) {
    Some(Table(nested)) => get_or_create_table(nested, rest_keys)
    Some(_) =>
      raise Failure("Key already exists with non-table value: \{first_key}")
    None => {
      let nested : Map[String, TomlValue] = {}
      table[first_key] = Table(nested)
      get_or_create_table(nested, rest_keys)
    }
  }
}

///|
/// Parse the entire TOML document
pub fn parse(input : String) -> TomlValue raise {
  let tokens = tokenize(input)
  let parser = Parser::new(tokens)
  let root : Map[String, TomlValue] = {}
  let mut current_table = root
  parser.skip_newlines()
  while true {
    match parser.peek() {
      TokEOF => return Table(root)

      // Table header [section]
      TokLeftBracket => {
        parser.next() |> ignore

        // Check if it's an array of tables [[section]]
        let is_array = match parser.peek() {
          TokLeftBracket => {
            parser.next() |> ignore
            true
          }
          _ => false
        }
        let keys = parser.parse_key_path()
        if is_array {
          parser.expect(TokRightBracket)
        }
        parser.expect(TokRightBracket)
        parser.skip_newlines()
        current_table = get_or_create_table(root, keys)
        continue
      }

      // Key-value pair
      TokIdent(_) => {
        let keys = parser.parse_key_path()
        parser.expect(TokEqual)
        let value = parser.parse_value()
        set_nested(current_table, keys, value)
        parser.skip_newlines()
        continue
      }
      TokNewline => {
        parser.next() |> ignore
        continue
      }
      tok => raise Failure("Unexpected token at top level: \{tok}")
    }
  } else {
    raise Failure("Unreachable")
  }
}

///|
/// Convert TomlValue to a pretty string
pub fn TomlValue::to_pretty_string(self : TomlValue) -> String {
  match self {
    String(s) => "\"\{s}\""
    Integer(i) => i.to_string()
    Float(f) => f.to_string()
    Boolean(b) => b.to_string()
    DateTime(dt) => dt
    Array(items) => {
      let parts = items.map(fn(v) { v.to_pretty_string() })
      let sep = ", "
      let joined = parts.join(sep)
      "[\{joined}]"
    }
    Table(map) => {
      let parts : Array[String] = []
      for k, v in map {
        parts.push("\{k} = \{v.to_pretty_string()}")
      }
      let sep = ", "
      let joined = parts.join(sep)
      "{ \{joined} }"
    }
  }
}