///|
using @tokenize {tokenize, type Token, type TomlDateTime}

///|
/// Skip newline tokens
fn Parser::skip_newlines(self : Parser) -> Unit {
  let next_view = loop self.view() {
    [Newline(..), .. rest] => continue rest
    rest => rest
  }
  self.update_view(next_view)
}

///|
/// Parse a primary value (string, number, boolean)
fn Parser::parse_value(
  self : Self,
  skip_newlines? : Bool = false,
) -> TomlValue raise {
  match self.view(skip_newlines~) {
    [StringToken(s, ..), .. rest] => {
      self.update_view(rest)
      TomlString(s)
    }
    [IntegerToken(i, ..), .. rest] => {
      self.update_view(rest)
      TomlInteger(i)
    }
    [FloatToken(f, ..), .. rest] => {
      self.update_view(rest)
      TomlFloat(f)
    }
    [BooleanToken(b, ..), .. rest] => {
      self.update_view(rest)
      TomlBoolean(b)
    }
    [DateTimeToken(dt, ..), .. rest] => {
      self.update_view(rest)
      TomlDateTime(dt)
    }
    // TOML Array
    [LeftBracket, .. rest] => {
      self.update_view(rest)
      self.parse_array()
    }
    // TOML Inline Table
    [LeftBrace, .. rest] => {
      self.update_view(rest)
      self.parse_inline_table()
    }
    [Identifier("inf", ..), .. rest] => {
      self.update_view(rest)
      TomlFloat(1.0 / 0.0) // positive infinity
    }
    [Identifier("nan", ..), .. rest] => {
      self.update_view(rest)
      TomlFloat(0.0 / 0.0) // NaN
    }
    _ => self.error("Expected value")
  }
}

///|
/// Parse an array [1, 2, 3]
fn Parser::parse_array(self : Self) -> TomlValue raise {
  let values = []
  while true {
    if self.view(skip_newlines=true) is [RightBracket, .. rest] {
      self.update_view(rest)
      return TomlArray(values)
    }
    // self.skip_newlines() // here Newline is allowed
    values.push(self.parse_value(skip_newlines=true))
    match self.view(skip_newlines=true) {
      [Comma, .. rest] => self.update_view(rest)
      [RightBracket, .. rest] => {
        self.update_view(rest)
        break
      }
      _ => self.error("Expected ',' or ']' in array")
    }
  }
  TomlArray(values)
}

///|
/// Parse an inline table {key = value, key2 = value2}
/// No trailing comma allowed unlike Array
fn Parser::parse_inline_table(self : Parser) -> TomlValue raise {
  let table = {}
  if self.view() is [RightBrace, .. rest] {
    self.update_view(rest)
    return TomlTable(table)
  }
  while true {
    // Parse dotted key
    let key_path = self.parse_dotted_key()

    // Expect =
    match self.view() {
      [Equals, .. rest] => self.update_view(rest)
      _ => self.error("Expected '='")
    }

    // Parse value
    let value = self.parse_value()
    set_dotted_key_value(table, key_path, value) catch {
      error => self.error(error.to_string())
    }
    match self.view() {
      [Comma, .. rest] => self.update_view(rest)
      [RightBrace, .. rest] => {
        self.update_view(rest)
        break
      }
      _ => self.error("Expected ',' or '}' in inline table")
    }
  }
  TomlTable(table)
}

///|
/// Parse a table path: section.subsection.key
fn Parser::parse_table_path(self : Parser) -> Array[String] raise {
  let path = Array::new()

  // Parse first key
  let first_key = match self.view() {
    [Identifier(name, ..), .. rest] => {
      self.update_view(rest)
      name
    }
    [StringToken(name, ..), .. rest] => {
      self.update_view(rest)
      name
    }
    [IntegerToken(i, ..), .. rest] => {
      self.update_view(rest)
      i.to_string()
    }
    _ => self.error("Expected path name")
  }
  path.push(first_key)

  // Parse additional keys separated by dots
  let next_view = loop self.view() {
    [Dot(..), .. rest] => {
      self.update_view(rest)
      let key = match self.view() {
        [Identifier(name, ..), .. rest2] => {
          self.update_view(rest2)
          name
        }
        [StringToken(name, ..), .. rest2] => {
          self.update_view(rest2)
          name
        }
        [IntegerToken(i, ..), .. rest2] => {
          self.update_view(rest2)
          i.to_string()
        }
        _ => self.error("Expected table name after dot")
      }
      path.push(key)
      continue self.view()
    }
    rest => rest
  }
  self.update_view(next_view)
  path
}

///|
fn[A] Parser::error(self : Parser, msg : String) -> A raise {
  match self.view() {
    [token, ..] => fail("\{msg} at \{@debug.to_string(token.loc())}")
    [] => fail("\{msg} at the end of input")
  }
}

///|
/// Parse a dotted key path: key.subkey.subsubkey
fn Parser::parse_dotted_key(self : Parser) -> Array[String] raise {
  let path = Array::new()

  // Parse first key - handle the case where the tokenizer saw a float like "1.2"
  let first_key = match self.view() {
    [Identifier(k), .. rest] => {
      self.update_view(rest)
      k
    }
    [StringToken(k), .. rest] => {
      self.update_view(rest)
      k
    }
    [IntegerToken(i), .. rest] => {
      self.update_view(rest)
      i.to_string()
    }
    [FloatToken(f), .. rest] => {
      // Handle float tokens that represent dotted numeric keys like "1.2"
      self.update_view(rest)
      let float_str = f.to_string()

      // Split the float string on the dot to create dotted keys
      let dot_index = match float_str.find(".") {
        Some(i) => i
        None => {
          // No dot found, treat as single key
          path.push(float_str)
          return path
        }
      }
      let first_part = float_str[:dot_index].to_string()
      let second_part = float_str[dot_index + 1:].to_string()
      path.push(first_part)
      path.push(second_part)

      // Continue parsing for more dots after this float
      let next_view = loop self.view() {
        [Dot(..), .. rest] => {
          self.update_view(rest)
          let key = match self.view() {
            [Identifier(name), .. rest2] => {
              self.update_view(rest2)
              name
            }
            [StringToken(name), .. rest2] => {
              self.update_view(rest2)
              name
            }
            [IntegerToken(i), .. rest2] => {
              self.update_view(rest2)
              i.to_string()
            }
            _ => self.error("Expected key after dot")
          }
          path.push(key)
          continue self.view()
        }
        rest => rest
      }
      self.update_view(next_view)
      return path
    }
    _ => self.error("Expected key")
  }
  path.push(first_key)

  // Parse additional keys separated by dots
  let next_view = loop self.view() {
    [Dot(..), .. rest] => {
      self.update_view(rest)
      let key = match self.view() {
        [Identifier(name), .. rest2] => {
          self.update_view(rest2)
          name
        }
        [StringToken(name), .. rest2] => {
          self.update_view(rest2)
          name
        }
        [IntegerToken(i), .. rest2] => {
          self.update_view(rest2)
          i.to_string()
        }
        _ => self.error("Expected key after dot")
      }
      path.push(key)
      continue self.view()
    }
    rest => rest
  }
  self.update_view(next_view)
  path
}

///|
/// Parse a key-value pair with dotted key support: key.subkey = value
fn Parser::parse_key_value(self : Parser) -> (Array[String], TomlValue) raise {
  // Parse dotted key
  let key_path = self.parse_dotted_key()

  // Expect =
  match self.view() {
    [Equals, .. rest] => self.update_view(rest)
    _ => self.error("Expected '='")
  }

  // Parse value
  let value = self.parse_value()
  (key_path, value)
}

///|
/// Parse a TOML document with support for tables
pub fn parse(input : String) -> TomlValue raise {
  let tokens = tokenize(input)
  let parser = Parser::new(tokens)
  let main_table = {}
  for current_table = main_table {
    parser.skip_newlines()
    match parser.view() {
      [EOF, ..] => break
      [LeftBracket, LeftBracket, .. rest] => {
        parser.update_view(rest)
        let table_path = parser.parse_table_path()
        match parser.view() {
          [RightBracket, RightBracket, .. rest] => parser.update_view(rest)
          _ => parser.error("Expected ']]'")
        }
        // Create or append to the array of tables structure
        let current_table = create_array_of_tables(main_table, table_path) catch {
          error => parser.error(error.to_string())
        }
        continue current_table
      }
      [LeftBracket, .. rest] => {
        parser.update_view(rest)
        let table_path = parser.parse_table_path()
        match parser.view() {
          [RightBracket, .. rest3] => parser.update_view(rest3)
          _ => parser.error("Expected ']'")
        }

        // Create or get the nested table structure
        let current_table = create_nested_table(main_table, table_path) catch {
          error => parser.error(error.to_string())
        }
        continue current_table
      }
      _ => {
        // Parse key-value pair
        let (key_path, value) = parser.parse_key_value()
        set_dotted_key_value(current_table, key_path, value) catch {
          error => parser.error(error.to_string())
        }
        // Validate that key-value pair is properly terminated
        match parser.view() {
          [EOF, ..] => () // EOF is valid
          [Newline, ..] => () // Newline is valid (comments are already skipped by tokenizer)
          _ =>
            parser.error("Expected newline or end of file after key-value pair")
        }
      }
    }
  }
  TomlTable(main_table)
}