///|
/// TOML Value types
pub(all) enum TomlValue {
  String(String)
  Integer(Int64)
  Float(Double)
  Boolean(Bool)
  DateTime(String) // ISO 8601 datetime as string
  Array(Array[TomlValue])
  Table(Map[String, TomlValue])
} derive(Show, Eq)

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

///|
/// Error types for TOML parsing
pub(all) suberror ParseError {
  UnexpectedChar(Int, Char)
  UnexpectedEof
  InvalidEscape(Int, Char)
  InvalidNumber(Int, String)
  InvalidKey(Int, String)
  InvalidValue(Int, String)
  DuplicateKey(String)
  InvalidTable(String)
} derive(Eq, Show)

///|
/// Token types for lexer
enum Token {
  // Literals
  TString(String)
  TInteger(Int64)
  TFloat(Double)
  TBoolean(Bool)
  TDateTime(String)

  // Symbols
  TLeftBracket // [
  TRightBracket // ]
  TLeftBrace // {
  TRightBrace // }
  TEquals // =
  TDot // .
  TComma // ,

  // Identifiers
  TIdent(String)

  // Special
  TNewline
  TEof
} 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 current character without consuming
fn Lexer::peek(self : Lexer) -> Char? {
  if self.pos >= self.input.length() {
    None
  } else {
    self.input.get_char(self.pos)
  }
}

///|
/// Peek ahead n characters
fn Lexer::peek_ahead(self : Lexer, n : Int) -> Char? {
  let pos = self.pos + n
  if pos >= self.input.length() {
    None
  } else {
    self.input.get_char(pos)
  }
}

///|
/// Advance position by n characters
fn Lexer::advance(self : Lexer, n : Int) -> Unit {
  for i = 0; i < n; i = i + 1 {
    match self.peek() {
      Some('\n') => {
        self.line = self.line + 1
        self.col = 1
      }
      Some(_) => self.col = self.col + 1
      None => ()
    }
    self.pos = self.pos + 1
  }
}

///|
/// Skip whitespace (space and tab only, not newlines)
fn Lexer::skip_whitespace(self : Lexer) -> Unit {
  while true {
    match self.peek() {
      Some(' ') | Some('\t') => self.advance(1)
      _ => break
    }
  }
}

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

///|
/// Check if character is valid for identifier start
fn is_ident_start(c : Char) -> Bool {
  match c {
    'a'..='z' | 'A'..='Z' | '_' | '-' => true
    _ => false
  }
}

///|
/// Check if character is valid for identifier continuation
fn is_ident_continue(c : Char) -> Bool {
  match c {
    'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' => true
    _ => false
  }
}

///|
/// Check if character is a digit
fn is_digit(c : Char) -> Bool {
  match c {
    '0'..='9' => true
    _ => false
  }
}

///|
/// Parse a bare key (unquoted identifier)
fn Lexer::parse_bare_key(self : Lexer) -> String raise ParseError {
  let start = self.pos
  match self.peek() {
    Some(c) if is_ident_start(c) => {
      self.advance(1)
      while true {
        match self.peek() {
          Some(c) if is_ident_continue(c) => self.advance(1)
          _ => break
        }
      }
      try! self.input[start:self.pos].to_string()
    }
    Some(c) =>
      raise ParseError::InvalidKey(self.pos, "Invalid key character: \{c}")
    None => raise ParseError::UnexpectedEof
  }
}

///|
/// Parse a basic string (double-quoted)
fn Lexer::parse_basic_string(self : Lexer) -> String raise ParseError {
  self.advance(1) // skip opening quote
  let mut result = ""
  while true {
    match self.peek() {
      Some('"') => {
        self.advance(1)
        break
      }
      Some('\\') => {
        self.advance(1)
        match self.peek() {
          Some('b') => {
            result = result + "\u{0008}"
            self.advance(1)
          }
          Some('t') => {
            result = result + "\t"
            self.advance(1)
          }
          Some('n') => {
            result = result + "\n"
            self.advance(1)
          }
          Some('f') => {
            result = result + "\u{000C}"
            self.advance(1)
          }
          Some('r') => {
            result = result + "\r"
            self.advance(1)
          }
          Some('"') => {
            result = result + "\""
            self.advance(1)
          }
          Some('\\') => {
            result = result + "\\"
            self.advance(1)
          }
          Some(c) => raise ParseError::InvalidEscape(self.pos, c)
          None => raise ParseError::UnexpectedEof
        }
      }
      Some('\n') =>
        raise ParseError::InvalidValue(self.pos, "Newline in basic string")
      Some(c) => {
        result = result + c.to_string()
        self.advance(1)
      }
      None => raise ParseError::UnexpectedEof
    }
  }
  result
}

///|
/// Parse a literal string (single-quoted, no escapes)
fn Lexer::parse_literal_string(self : Lexer) -> String raise ParseError {
  self.advance(1) // skip opening quote
  let start = self.pos
  while true {
    match self.peek() {
      Some('\'') => {
        let result = try! self.input[start:self.pos].to_string()
        self.advance(1)
        return result
      }
      Some('\n') =>
        raise ParseError::InvalidValue(self.pos, "Newline in literal string")
      Some(_) => self.advance(1)
      None => raise ParseError::UnexpectedEof
    }
  } else {
    raise ParseError::UnexpectedEof
  }
}

///|
/// Parse a number (integer or float)
fn Lexer::parse_number(self : Lexer) -> Token raise ParseError {
  let start = self.pos
  let mut has_dot = false
  let mut has_exp = false

  // Handle sign
  match self.peek() {
    Some('+') | Some('-') => self.advance(1)
    _ => ()
  }

  // Parse digits
  while true {
    match self.peek() {
      Some(c) if is_digit(c) => self.advance(1)
      Some('_') => self.advance(1) // Allow underscores in numbers
      Some('.') if not(has_dot) && not(has_exp) => {
        has_dot = true
        self.advance(1)
      }
      Some('e') | Some('E') if not(has_exp) => {
        has_exp = true
        has_dot = true // Exponent implies float
        self.advance(1)
        // Handle exponent sign
        match self.peek() {
          Some('+') | Some('-') => self.advance(1)
          _ => ()
        }
      }
      _ => break
    }
  }
  let view = try! self.input[start:self.pos]
  let num_str = view.to_string().replace(old="_", new="")
  if has_dot || has_exp {
    let f = @strconv.parse_double(num_str[:]) catch {
      _ => raise ParseError::InvalidNumber(start, num_str)
    }
    TFloat(f)
  } else {
    let i = @strconv.parse_int64(num_str[:]) catch {
      _ => raise ParseError::InvalidNumber(start, num_str)
    }
    TInteger(i)
  }
}

///|
/// Get next token
pub fn Lexer::next_token(self : Lexer) -> Token raise ParseError {
  while true {
    self.skip_whitespace()
    match self.peek() {
      None => return TEof
      Some('#') => self.skip_comment()
      Some('\n') | Some('\r') => {
        self.advance(1)
        return TNewline
      }
      Some('[') => {
        self.advance(1)
        return TLeftBracket
      }
      Some(']') => {
        self.advance(1)
        return TRightBracket
      }
      Some('{') => {
        self.advance(1)
        return TLeftBrace
      }
      Some('}') => {
        self.advance(1)
        return TRightBrace
      }
      Some('=') => {
        self.advance(1)
        return TEquals
      }
      Some('.') => {
        self.advance(1)
        return TDot
      }
      Some(',') => {
        self.advance(1)
        return TComma
      }
      Some('"') => return TString(self.parse_basic_string())
      Some('\'') => return TString(self.parse_literal_string())
      Some(c) if is_digit(c) || c == '+' || c == '-' =>
        return self.parse_number()
      Some('t') | Some('f') =>
        // Try to parse boolean
        if self.peek() == Some('t') &&
          self.peek_ahead(1) == Some('r') &&
          self.peek_ahead(2) == Some('u') &&
          self.peek_ahead(3) == Some('e') {
          self.advance(4)
          return TBoolean(true)
        } else if self.peek() == Some('f') &&
          self.peek_ahead(1) == Some('a') &&
          self.peek_ahead(2) == Some('l') &&
          self.peek_ahead(3) == Some('s') &&
          self.peek_ahead(4) == Some('e') {
          self.advance(5)
          return TBoolean(false)
        } else {
          // Not a boolean, parse as identifier
          return TIdent(self.parse_bare_key())
        }
      Some(c) if is_ident_start(c) => return TIdent(self.parse_bare_key())
      Some(c) => raise ParseError::UnexpectedChar(self.pos, c)
    }
  } else {
    TEof
  }
}

///|
/// Parser state
struct Parser {
  lexer : Lexer
  mut current : Token
  mut peek_tok : Token
}

///|
/// Create a new parser
pub fn Parser::new(input : String) -> Parser raise ParseError {
  let lexer = Lexer::new(input)
  let p = { lexer, current: TEof, peek_tok: TEof }
  p.advance() // Load first token
  p.advance() // Load second token
  p
}

///|
/// Advance to next token
fn Parser::advance(self : Parser) -> Unit raise ParseError {
  self.current = self.peek_tok
  self.peek_tok = self.lexer.next_token()
}

///|
/// Skip newlines
fn Parser::skip_newlines(self : Parser) -> Unit raise ParseError {
  while true {
    match self.current {
      TNewline => self.advance()
      _ => break
    }
  }
}

///|
/// Parse a key (possibly dotted)
fn Parser::parse_key(self : Parser) -> Array[String] raise ParseError {
  let keys = []
  while true {
    match self.current {
      TIdent(name) => {
        keys.push(name)
        self.advance()
      }
      TString(name) => {
        keys.push(name)
        self.advance()
      }
      _ => raise ParseError::InvalidKey(self.lexer.pos, "Expected key")
    }
    match self.current {
      TDot => {
        self.advance()
        continue
      }
      _ => return keys
    }
  } else {
    keys
  }
}

///|
/// Parse an array
fn Parser::parse_array(self : Parser) -> Array[TomlValue] raise ParseError {
  self.advance() // skip [
  self.skip_newlines()
  let values = []
  while true {
    match self.current {
      TRightBracket => {
        self.advance()
        return values
      }
      _ => {
        values.push(self.parse_value())
        self.skip_newlines()
        match self.current {
          TComma => {
            self.advance()
            self.skip_newlines()
          }
          TRightBracket => ()
          _ =>
            raise ParseError::InvalidValue(
              self.lexer.pos,
              "Expected , or ] in array",
            )
        }
      }
    }
  } else {
    values
  }
}

///|
/// Parse an inline table
fn Parser::parse_inline_table(
  self : Parser,
) -> Map[String, TomlValue] raise ParseError {
  self.advance() // skip {
  let table : Map[String, TomlValue] = {}
  while true {
    match self.current {
      TRightBrace => {
        self.advance()
        return table
      }
      _ => {
        let keys = self.parse_key()
        match self.current {
          TEquals => self.advance()
          _ =>
            raise ParseError::InvalidValue(
              self.lexer.pos,
              "Expected = in inline table",
            )
        }
        let value = self.parse_value()

        // Insert value at nested key path
        if keys.length() == 1 {
          table[keys[0]] = value
        } else {
          // Handle nested keys in inline table
          let mut current = table
          for i = 0; i < keys.length() - 1; i = i + 1 {
            let key = keys[i]
            match current.get(key) {
              Some(Table(tbl)) => current = tbl
              None => {
                let new_table : Map[String, TomlValue] = {}
                current[key] = Table(new_table)
                current = new_table
              }
              _ => raise ParseError::DuplicateKey(keys.join("."))
            }
          }
          current[keys[keys.length() - 1]] = value
        }
        match self.current {
          TComma => self.advance()
          TRightBrace => ()
          _ =>
            raise ParseError::InvalidValue(
              self.lexer.pos,
              "Expected , or } in inline table",
            )
        }
      }
    }
  } else {
    table
  }
}

///|
/// Parse a value
fn Parser::parse_value(self : Parser) -> TomlValue raise ParseError {
  match self.current {
    TString(s) => {
      self.advance()
      String(s)
    }
    TInteger(i) => {
      self.advance()
      Integer(i)
    }
    TFloat(f) => {
      self.advance()
      Float(f)
    }
    TBoolean(b) => {
      self.advance()
      Boolean(b)
    }
    TLeftBracket => Array(self.parse_array())
    TLeftBrace => Table(self.parse_inline_table())
    _ => raise ParseError::InvalidValue(self.lexer.pos, "Expected value")
  }
}

///|
/// Insert value at dotted key path
fn insert_at_path(
  table : Map[String, TomlValue],
  keys : Array[String],
  value : TomlValue,
) -> Unit raise ParseError {
  if keys.length() == 0 {
    raise ParseError::InvalidKey(0, "Empty key path")
  }
  if keys.length() == 1 {
    if table.contains(keys[0]) {
      raise ParseError::DuplicateKey(keys[0])
    }
    table[keys[0]] = value
  } else {
    let key = keys[0]
    let rest = keys[1:]
    match table.get(key) {
      Some(Table(nested)) => insert_at_path(nested, rest.to_array(), value)
      None => {
        let nested : Map[String, TomlValue] = {}
        table[key] = Table(nested)
        insert_at_path(nested, rest.to_array(), value)
      }
      _ => raise ParseError::DuplicateKey(keys.join("."))
    }
  }
}

///|
/// Get or create table at path
fn get_or_create_table(
  table : Map[String, TomlValue],
  keys : Array[String],
) -> Map[String, TomlValue] raise ParseError {
  if keys.length() == 0 {
    return table
  }
  let key = keys[0]
  let rest = keys[1:]
  match table.get(key) {
    Some(Table(nested)) =>
      if rest.length() == 0 {
        nested
      } else {
        get_or_create_table(nested, rest.to_array())
      }
    None => {
      let nested : Map[String, TomlValue] = {}
      table[key] = Table(nested)
      if rest.length() == 0 {
        nested
      } else {
        get_or_create_table(nested, rest.to_array())
      }
    }
    _ => raise ParseError::InvalidTable(keys.join("."))
  }
}

///|
/// Parse TOML document
pub fn parse(input : String) -> Map[String, TomlValue] raise ParseError {
  let parser = Parser::new(input)
  let root : Map[String, TomlValue] = {}
  let mut current_table = root
  parser.skip_newlines()
  while true {
    match parser.current {
      TEof => return root
      TNewline => parser.advance()
      TLeftBracket => {
        parser.advance()
        // Check for array of tables [[...]]
        let is_array = match parser.current {
          TLeftBracket => {
            parser.advance()
            true
          }
          _ => false
        }
        let keys = parser.parse_key()
        if is_array {
          // Array of tables
          match parser.current {
            TRightBracket => parser.advance()
            _ =>
              raise ParseError::InvalidTable("Expected ] for array of tables")
          }
          match parser.current {
            TRightBracket => parser.advance()
            _ =>
              raise ParseError::InvalidTable("Expected ]] for array of tables")
          }

          // Create new table in array
          let new_table : Map[String, TomlValue] = {}
          if keys.length() == 1 {
            match root.get(keys[0]) {
              Some(Array(arr)) => arr.push(Table(new_table))
              None => root[keys[0]] = Array([Table(new_table)])
              _ =>
                raise ParseError::InvalidTable(
                  "Key already exists as non-array",
                )
            }
            current_table = new_table
          } else {
            let parent_keys = keys[0:keys.length() - 1].to_array()
            let array_key = keys[keys.length() - 1]
            let parent = get_or_create_table(root, parent_keys)
            match parent.get(array_key) {
              Some(Array(arr)) => arr.push(Table(new_table))
              None => parent[array_key] = Array([Table(new_table)])
              _ =>
                raise ParseError::InvalidTable(
                  "Key already exists as non-array",
                )
            }
            current_table = new_table
          }
        } else {
          // Regular table [...]
          match parser.current {
            TRightBracket => parser.advance()
            _ => raise ParseError::InvalidTable("Expected ]")
          }
          current_table = get_or_create_table(root, keys)
        }
        parser.skip_newlines()
      }
      _ => {
        // Key-value pair
        let keys = parser.parse_key()
        match parser.current {
          TEquals => parser.advance()
          _ => raise ParseError::InvalidValue(parser.lexer.pos, "Expected =")
        }
        let value = parser.parse_value()
        insert_at_path(current_table, keys, value)
        parser.skip_newlines()
      }
    }
  } else {
    root
  }
}

///|
/// Parse TOML string and return Result
pub fn parse_toml(input : String) -> Result[Map[String, TomlValue], ParseError] {
  Ok(parse(input)) catch {
    err => Err(err)
  }
}