///|
suberror ParserError {
  UnexpectedTokenError(Token, Token)
  UnexpectedTokenMessageError(Token, String)
  MultipleElseInCaseExprError
  UnimplementedError(String)
  InternalBug(String)
  InvalidDataTypeError(String)
} derive(Show)

///|
type ParserResult[T] = (T, ArrayView[Token])

///|
pub struct Parser {
  dialect : &Dialect
  // recursion counter
}

///|
fn[T, U] ParserResult::map(
  self : ParserResult[T],
  f : (T) -> U,
) -> ParserResult[U] {
  let (result, tokens) = self
  (f(result), tokens)
}

///|
fn Parser::expect_token(
  _self : Parser,
  tokens : ArrayView[Token],
  expected : Token,
) -> ArrayView[Token] raise ParserError {
  match tokens {
    [token, .. tokens] if token == expected => tokens
    [token, .. _tokens] => raise UnexpectedTokenError(token, expected)
    [] => raise UnexpectedTokenError(Token::Eof, expected)
  }
}

///|
pub fn parse(
  tokens : ArrayView[Token],
  dialect? : &Dialect = MySQL::{  },
) -> Array[Statement] raise ParserError {
  let parser = Parser::{ dialect, }
  let statements = []
  loop tokens {
    [Token::Eof] => break
    [] => break
    tokens => {
      let (stmt, tokens) = parser.parse_statement(tokens)
      statements.push(stmt)
      continue tokens
    }
  }
  statements
}

///|
fn Parser::parse_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[Statement] raise ParserError {
  // Try dialect-specific parsing first
  let dialect_result = self.dialect.parse_statement(self, tokens)
  match dialect_result {
    Some(result) => return result
    None => ()
  }
  let (stmt, tokens) = match tokens {
    [Keyword(Select), .. _rest] =>
      self.parse_query(tokens).map(stmt => Statement::Query(stmt))
    [Keyword(With), .. _rest] =>
      self.parse_query(tokens).map(stmt => Statement::Query(stmt))
    [Keyword(Show), .. _rest] =>
      self.parse_show_statement(tokens).map(stmt => Statement::Show(stmt))
    [Keyword(Set), .. _rest] =>
      self.parse_set_statement(tokens).map(stmt => Statement::Set(stmt))
    [Keyword(Create), Keyword(Table), .. _rest] =>
      self
      .parse_create_table_statement(tokens)
      .map(stmt => Statement::CreateTable(stmt))
    [Keyword(Create), Keyword(View), .. _rest] =>
      self
      .parse_create_view_statement(tokens)
      .map(stmt => Statement::CreateView(stmt))
    [Keyword(Create), Keyword(Index), .. _rest] =>
      self
      .parse_create_index_statement(tokens)
      .map(stmt => Statement::CreateIndex(stmt))
    [Keyword(Create), Keyword(Unique), Keyword(Index), .. _rest] =>
      self
      .parse_create_index_statement(tokens)
      .map(stmt => Statement::CreateIndex(stmt))
    [Keyword(Create), Keyword(Database), .. _rest] =>
      self
      .parse_create_database_statement(tokens)
      .map(stmt => Statement::CreateDatabase(stmt))
    [Keyword(Create), Keyword(Schema), .. _rest] =>
      self
      .parse_create_schema_statement(tokens)
      .map(stmt => Statement::CreateSchema(stmt))
    [Keyword(Create), Keyword(Function), .. _rest] =>
      self
      .parse_create_function_statement(tokens)
      .map(stmt => Statement::CreateFunction(stmt))
    [Keyword(Create), Keyword(Procedure), .. _rest] =>
      self
      .parse_create_procedure_statement(tokens)
      .map(stmt => Statement::CreateProcedure(stmt))
    [Keyword(Create), Keyword(Sequence), .. _rest] =>
      self
      .parse_create_sequence_statement(tokens)
      .map(stmt => Statement::CreateSequence(stmt))
    [Keyword(Create), Keyword(Temporary), Keyword(Sequence), .. _rest] =>
      self
      .parse_create_sequence_statement(tokens)
      .map(stmt => Statement::CreateSequence(stmt))
    [Keyword(Create), Keyword(Temp), Keyword(Sequence), .. _rest] =>
      self
      .parse_create_sequence_statement(tokens)
      .map(stmt => Statement::CreateSequence(stmt))
    [Keyword(Drop), Keyword(View), .. _rest] =>
      self
      .parse_drop_view_statement(tokens)
      .map(stmt => Statement::DropView(stmt))
    [Keyword(Drop), Keyword(Table), .. _rest] =>
      self
      .parse_drop_table_statement(tokens)
      .map(stmt => Statement::DropTable(stmt))
    [Keyword(Drop), Keyword(Index), .. _rest] =>
      self
      .parse_drop_index_statement(tokens)
      .map(stmt => Statement::DropIndex(stmt))
    [Keyword(Insert), .. _rest] =>
      self.parse_insert_statement(tokens).map(stmt => Statement::Insert(stmt))
    [Keyword(Replace), .. _rest] =>
      self.parse_replace_statement(tokens).map(stmt => Statement::Insert(stmt))
    [Keyword(Delete), .. _rest] =>
      self.parse_delete_statement(tokens).map(stmt => Statement::Delete(stmt))
    [Keyword(Update), .. _rest] =>
      self.parse_update_statement(tokens).map(stmt => Statement::Update(stmt))
    [Keyword(Merge), .. _rest] =>
      self.parse_merge_statement(tokens).map(stmt => Statement::Merge(stmt))
    [Keyword(Truncate), .. _rest] =>
      self
      .parse_truncate_statement(tokens)
      .map(stmt => Statement::Truncate(stmt))
    [Keyword(Alter), Keyword(Table), .. _rest] =>
      self
      .parse_alter_table_statement(tokens)
      .map(stmt => Statement::AlterTable(stmt))
    [Keyword(Alter), Keyword(Index), .. _rest] =>
      self
      .parse_alter_index_statement(tokens)
      .map(stmt => Statement::AlterIndex(stmt))
    [Keyword(Begin), .. _rest] =>
      self.parse_begin_statement(tokens).map(stmt => Statement::Begin(stmt))
    [Keyword(Start), Keyword(Transaction), .. _rest] =>
      self
      .parse_start_transaction_statement(tokens)
      .map(stmt => Statement::Begin(stmt))
    [Keyword(Commit), .. _rest] =>
      self.parse_commit_statement(tokens).map(stmt => Statement::Commit(stmt))
    [Keyword(Rollback), .. _rest] =>
      self
      .parse_rollback_statement(tokens)
      .map(stmt => Statement::Rollback(stmt))
    [Keyword(Savepoint), .. _rest] =>
      self
      .parse_savepoint_statement(tokens)
      .map(stmt => Statement::Savepoint(stmt))
    [Keyword(Release), Keyword(Savepoint), .. _rest] =>
      self
      .parse_release_savepoint_statement(tokens)
      .map(stmt => Statement::ReleaseSavepoint(stmt))
    [Keyword(Release), .. _rest] =>
      self
      .parse_release_savepoint_statement(tokens)
      .map(stmt => Statement::ReleaseSavepoint(stmt))
    [Keyword(Grant), .. _rest] =>
      self.parse_grant_statement(tokens).map(stmt => Statement::Grant(stmt))
    [Keyword(Revoke), .. _rest] =>
      self.parse_revoke_statement(tokens).map(stmt => Statement::Revoke(stmt))
    [Keyword(Use), .. _rest] =>
      self.parse_use_statement(tokens).map(stmt => Statement::Use(stmt))
    [Keyword(Copy), .. _rest] =>
      self.parse_copy_statement(tokens).map(stmt => Statement::Copy(stmt))
    [Keyword(Prepare), .. _rest] =>
      self.parse_prepare_statement(tokens).map(stmt => Statement::Prepare(stmt))
    [Keyword(Execute), .. _rest] =>
      self.parse_execute_statement(tokens).map(stmt => Statement::Execute(stmt))
    [Keyword(Deallocate), .. _rest] =>
      self
      .parse_deallocate_statement(tokens)
      .map(stmt => Statement::Deallocate(stmt))
    [LParen, .. rest] => {
      let (stmt, tokens) = self.parse_statement(rest)
      let tokens = self.expect_token(tokens, RParen)
      (stmt, tokens)
    }
    _ => raise UnexpectedTokenMessageError(tokens[0], "start of a statement")
  }
  let tokens = if tokens is [Semicolon, .. tokens] { tokens } else { tokens }
  (stmt, tokens)
}

///|

///|
fn Parser::parse_show_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[ShowStmt] raise ParserError {
  let tokens = self.expect_token(tokens, Keyword(Show))

  // Parse optional modifiers (EXTENDED, FULL, GLOBAL/SESSION)
  let mut extended = false
  let mut full = false
  let mut global_scope = false
  let tokens = match tokens {
    [Keyword(Extended), .. tokens] => {
      extended = true
      tokens
    }
    _ => tokens
  }
  let tokens = match tokens {
    [Keyword(Full), .. tokens] => {
      full = true
      tokens
    }
    _ => tokens
  }
  let tokens = match tokens {
    [Keyword(Global), .. tokens] => {
      global_scope = true
      tokens
    }
    [Keyword(Session), .. tokens] => tokens // SESSION is default
    _ => tokens
  }
  let (show_type, tokens) = match tokens {
    [Keyword(Tables), .. tokens] => (ShowType::Tables, tokens)
    [Keyword(Columns), .. tokens] => (ShowType::Columns, tokens)
    [Keyword(Status), .. tokens] => (ShowType::Status, tokens)
    [Keyword(Databases), .. tokens] => (ShowType::Databases, tokens)
    [Keyword(Schemas), .. tokens] => (ShowType::Schemas, tokens)
    [Keyword(Variables), .. tokens] => (ShowType::Variables, tokens)
    [Keyword(Processlist), .. tokens] => (ShowType::Processlist, tokens)
    [Keyword(Grants), .. tokens] => (ShowType::Grants, tokens)
    [Keyword(Functions), .. tokens] => (ShowType::Functions, tokens)
    [Keyword(Create), Keyword(Table), .. tokens] => {
      // For SHOW CREATE TABLE, the table name comes directly after, not after FROM
      let (obj_name, tokens) = self.parse_object_name(tokens)
      return (
        {
          show_type: ShowType::CreateTable,
          object: Some(obj_name),
          filter: None,
          extended,
          full,
          global_scope,
        },
        tokens,
      )
    }
    [Keyword(Create), Keyword(View), .. tokens] => {
      // For SHOW CREATE VIEW, the view name comes directly after, not after FROM  
      let (obj_name, tokens) = self.parse_object_name(tokens)
      return (
        {
          show_type: ShowType::CreateView,
          object: Some(obj_name),
          filter: None,
          extended,
          full,
          global_scope,
        },
        tokens,
      )
    }
    [Keyword(Create), Keyword(Functions), .. tokens] =>
      (ShowType::CreateFunction, tokens)
    [Keyword(Create), Keyword(Procedure), .. tokens] =>
      (ShowType::CreateProcedure, tokens)
    [Keyword(Create), Keyword(Event), .. tokens] =>
      (ShowType::CreateEvent, tokens)
    [Keyword(Create), Keyword(Trigger), .. tokens] =>
      (ShowType::CreateTrigger, tokens)
    [token, .. _tokens] =>
      raise UnexpectedTokenMessageError(token, "expected SHOW statement type")
    [] => raise InternalBug("parse_show_statement: unexpected end of tokens")
  }

  // Parse optional FROM clause
  let (object, tokens) = match tokens {
    [Keyword(From), .. tokens] => {
      let (obj_name, tokens) = self.parse_object_name(tokens)
      (Some(obj_name), tokens)
    }
    _ => (None, tokens)
  }

  // Parse optional filter (LIKE or WHERE)
  let (filter, tokens) = match tokens {
    [Keyword(Like), StringLiteral(pattern), .. tokens] =>
      (Some(ShowFilter::Like(pattern)), tokens)
    [Keyword(Where), .. tokens] => {
      let (expr, tokens) = self.parse_expr(tokens)
      (Some(ShowFilter::Where(expr)), tokens)
    }
    _ => (None, tokens)
  }
  ({ show_type, object, filter, extended, full, global_scope }, tokens)
}

///|
fn Parser::parse_set_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[SetStmt] raise ParserError {
  let tokens = self.expect_token(tokens, Keyword(Set))

  // Parse scope (GLOBAL, SESSION, or default)
  let (scope, tokens) = match tokens {
    [Keyword(Global), .. tokens] => (SetScope::Global, tokens)
    [Keyword(Session), .. tokens] => (SetScope::Session, tokens)
    _ => (SetScope::Local, tokens) // Default scope
  }

  // Parse variable assignments
  let (assignments, tokens) = self.parse_set_assignments(tokens)

  // Determine final scope based on first variable name (if any user vars, use UserVar scope)
  let final_scope = if assignments.length() > 0 &&
    assignments[0].variable.strip_prefix("@") != None {
    SetScope::UserVar
  } else {
    scope
  }
  ({ scope: final_scope, assignments }, tokens)
}

///|
fn Parser::parse_set_assignments(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[Array[SetAssignment]] raise ParserError {
  let assignments : Array[SetAssignment] = []

  // Parse first assignment
  let (variable, tokens) = match tokens {
    [Identifier(name), .. rest] => (name, rest)
    [Token::Placeholder(name), .. rest] => (name, rest) // Support @variable syntax
    [token, ..] =>
      raise UnexpectedTokenMessageError(token, "expected variable name")
    [] => raise InternalBug("parse_set_assignments: unexpected end of tokens")
  }
  let tokens = self.expect_token(tokens, Eq)
  let (value, tokens) = self.parse_expr(tokens)
  assignments.push({ variable, value })

  // Parse additional assignments
  loop tokens {
    [Comma, .. rest] => {
      let (variable, remaining_tokens) = match rest {
        [Identifier(name), .. remaining] => (name, remaining)
        [Token::Placeholder(name), .. remaining] => (name, remaining) // Support @variable syntax
        [token, ..] =>
          raise UnexpectedTokenMessageError(token, "expected variable name")
        [] =>
          raise InternalBug(
            "parse_set_assignments: unexpected end of tokens after comma",
          )
      }
      let remaining_tokens = self.expect_token(remaining_tokens, Eq)
      let (value, remaining_tokens) = self.parse_expr(remaining_tokens)
      assignments.push({ variable, value })
      continue remaining_tokens
    }
    tokens => break (assignments, tokens)
  }
}

///|
fn Parser::parse_replace_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[InsertStmt] raise ParserError {
  let tokens = self.expect_token(tokens, Keyword(Replace))
  let tokens = self.expect_token(tokens, Keyword(Into))

  // Parse table name
  let (table_name, tokens) = self.parse_object_name(tokens)

  // Parse optional column list: (col1, col2, ...)
  let (columns, tokens) = match tokens {
    [LParen, .. tokens] => {
      let columns = []
      let tokens = loop tokens {
        [Identifier(col), Comma, .. tokens] => {
          columns.push(col)
          continue tokens
        }
        [Identifier(col), RParen, .. tokens] => {
          columns.push(col)
          break tokens
        }
        [token, .. _tokens] =>
          raise UnexpectedTokenMessageError(
            token, "expected column name in REPLACE column list",
          )
        [] =>
          raise InternalBug("parse_replace_statement: unexpected end of tokens")
      }
      (columns, tokens)
    }
    _ => ([], tokens)
  }

  // Parse VALUES (...) or SELECT query
  let (source, tokens) = match tokens {
    [Keyword(Values), .. tokens] => {
      let values = []
      let tokens = loop tokens {
        [LParen, .. tokens] => {
          let (exprs, tokens) = self.parse_comma_separated_exprs(tokens)
          let tokens = self.expect_token(tokens, RParen)
          values.push(exprs)
          match tokens {
            [Comma, .. tokens] => continue tokens
            tokens => break tokens
          }
        }
        tokens => break tokens
      }
      (InsertSource::Values(values), tokens)
    }
    [Keyword(Select), .. _rest] => {
      let (query, tokens) = self.parse_query(tokens)
      (InsertSource::Query(query), tokens)
    }
    _ => raise UnexpectedTokenError(tokens[0], Keyword(Values))
  }

  // Parse optional ON DUPLICATE KEY UPDATE clause (even for REPLACE)
  let (on_insert, tokens) = match tokens {
    [Keyword(On), Keyword(Duplicate), Keyword(Key), Keyword(Update), .. tokens] => {
      let (assignments, tokens) = self.parse_duplicate_key_assignments(tokens)
      (Some(OnInsert::DuplicateKeyUpdate(assignments)), tokens)
    }
    _ => (None, tokens)
  }
  (
    {
      table_name,
      columns,
      source,
      or: Some(SqliteOnConflict::Replace),
      on: on_insert,
    },
    tokens,
  )
}

///|
fn Parser::parse_copy_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[CopyStmt] raise ParserError {
  let tokens = self.expect_token(tokens, Keyword(Copy))

  // Parse source (table or query)
  let (source, tokens) = match tokens {
    [LParen, .. tokens] => {
      // COPY (SELECT ...) TO/FROM ...
      let (query, tokens) = self.parse_query(tokens)
      let tokens = self.expect_token(tokens, RParen)
      (CopySource::Query(query), tokens)
    }
    _ => {
      // COPY table_name [(column_list)] TO/FROM ...
      let (table_name, tokens) = self.parse_object_name(tokens)

      // Parse optional column list
      let (columns, tokens) = match tokens {
        [LParen, .. tokens] => {
          let columns = []
          let tokens = loop tokens {
            [Identifier(col), Comma, .. tokens] => {
              columns.push(col)
              continue tokens
            }
            [Identifier(col), RParen, .. tokens] => {
              columns.push(col)
              break tokens
            }
            [token, .. _tokens] =>
              raise UnexpectedTokenMessageError(
                token, "expected column name in COPY column list",
              )
            [] =>
              raise InternalBug(
                "parse_copy_statement: unexpected end of tokens in column list",
              )
          }
          (Some(columns), tokens)
        }
        _ => (None, tokens)
      }
      (CopySource::Table(table_name, columns), tokens)
    }
  }

  // Parse direction (TO or FROM)
  let (direction, tokens) = match tokens {
    [Keyword(To), .. tokens] => (CopyDirection::To, tokens)
    [Keyword(From), .. tokens] => (CopyDirection::From, tokens)
    [token, .. _tokens] =>
      raise UnexpectedTokenMessageError(
        token, "expected TO or FROM in COPY statement",
      )
    [] =>
      raise InternalBug(
        "parse_copy_statement: unexpected end of tokens after source",
      )
  }

  // Parse target (file, stdin, stdout, or program)
  let (target, tokens) = match tokens {
    [Keyword(Stdin), .. tokens] => (CopyTarget::Stdin, tokens)
    [Keyword(Stdout), .. tokens] => (CopyTarget::Stdout, tokens)
    [Keyword(Program), StringLiteral(command), .. tokens] =>
      (CopyTarget::Program(command), tokens)
    [StringLiteral(filename), .. tokens] => (CopyTarget::File(filename), tokens)
    [token, .. _tokens] =>
      raise UnexpectedTokenMessageError(
        token, "expected filename, STDIN, STDOUT, or PROGRAM in COPY statement",
      )
    [] =>
      raise InternalBug(
        "parse_copy_statement: unexpected end of tokens after direction",
      )
  }

  // Parse optional format options
  let (format_options, tokens) = match tokens {
    [LParen, .. tokens] => self.parse_copy_options(tokens)
    _ => ([], tokens)
  }
  ({ source, direction, target, format_options }, tokens)
}

///|
fn Parser::parse_copy_options(
  _self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[Array[CopyOption]] raise ParserError {
  let options = []
  let tokens = loop tokens {
    [RParen, .. tokens] => break tokens
    [Keyword(Format), Identifier("csv"), .. tokens] => {
      options.push(CopyOption::Format(CopyFormat::Csv))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(Format), Identifier("text"), .. tokens] => {
      options.push(CopyOption::Format(CopyFormat::Text))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(Format), Identifier("binary"), .. tokens] => {
      options.push(CopyOption::Format(CopyFormat::Binary))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Identifier("delimiter"), StringLiteral(delim), .. tokens] => {
      options.push(CopyOption::Delimiter(delim))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Identifier("quote"), StringLiteral(quote_char), .. tokens] => {
      options.push(CopyOption::Quote(quote_char))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Identifier("escape"), StringLiteral(escape_char), .. tokens] => {
      options.push(CopyOption::Escape(escape_char))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(Null), StringLiteral(null_str), .. tokens] => {
      options.push(CopyOption::Null(null_str))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(Header), Boolean(value), .. tokens] => {
      options.push(CopyOption::Header(Some(value)))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(Header), .. tokens] => {
      options.push(CopyOption::Header(None))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(ForceQuote), Mul, .. tokens] => {
      options.push(CopyOption::ForceQuote(CopyForceQuote::All))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(ForceQuote), LParen, .. tokens] => {
      let columns = []
      let tokens = loop tokens {
        [Identifier(col), Comma, .. tokens] => {
          columns.push(col)
          continue tokens
        }
        [Identifier(col), RParen, .. tokens] => {
          columns.push(col)
          break tokens
        }
        [token, .. _tokens] =>
          raise UnexpectedTokenMessageError(
            token, "expected column name in FORCE_QUOTE",
          )
        [] =>
          raise InternalBug("parse_copy_options: unexpected end in FORCE_QUOTE")
      }
      options.push(CopyOption::ForceQuote(CopyForceQuote::Columns(columns)))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(ForceNotNull), LParen, .. tokens] => {
      let columns = []
      let tokens = loop tokens {
        [Identifier(col), Comma, .. tokens] => {
          columns.push(col)
          continue tokens
        }
        [Identifier(col), RParen, .. tokens] => {
          columns.push(col)
          break tokens
        }
        [token, .. _tokens] =>
          raise UnexpectedTokenMessageError(
            token, "expected column name in FORCE_NOT_NULL",
          )
        [] =>
          raise InternalBug(
            "parse_copy_options: unexpected end in FORCE_NOT_NULL",
          )
      }
      options.push(CopyOption::ForceNotNull(columns))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(ForceNull), LParen, .. tokens] => {
      let columns = []
      let tokens = loop tokens {
        [Identifier(col), Comma, .. tokens] => {
          columns.push(col)
          continue tokens
        }
        [Identifier(col), RParen, .. tokens] => {
          columns.push(col)
          break tokens
        }
        [token, .. _tokens] =>
          raise UnexpectedTokenMessageError(
            token, "expected column name in FORCE_NULL",
          )
        [] =>
          raise InternalBug("parse_copy_options: unexpected end in FORCE_NULL")
      }
      options.push(CopyOption::ForceNull(columns))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [Keyword(Encoding), StringLiteral(encoding), .. tokens] => {
      options.push(CopyOption::Encoding(encoding))
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => continue tokens
      }
    }
    [token, .. _tokens] =>
      raise UnexpectedTokenMessageError(token, "expected COPY option")
    [] => raise InternalBug("parse_copy_options: unexpected end of tokens")
  }
  (options, tokens)
}

///|
fn Parser::parse_use_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[UseStmt] raise ParserError {
  let tokens = self.expect_token(tokens, Keyword(Use))

  // Parse database name
  let (database_name, tokens) = self.parse_object_name(tokens)
  ({ database_name, }, tokens)
}

///|
fn Parser::parse_comma_separated_exprs(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[Array[Expr]] raise ParserError {
  let exprs = []
  let (expr, tokens) = self.parse_expr(tokens)
  exprs.push(expr)
  let tokens = loop tokens {
    [Comma, .. tokens] => {
      let (expr, tokens) = self.parse_expr(tokens)
      exprs.push(expr)
      continue tokens
    }
    tokens => break tokens
  }
  (exprs, tokens)
}

///|
test "Parse simple select statement" {
  let tokens = [
    Keyword(Select),
    Token::Mul,
    Keyword(From),
    Token::Identifier("t"),
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(
    stmt,
    content=(
      #|SELECT
      #|  *
      #|FROM
      #|  t;
    ),
  )
}

///|
test "Parse USE database statement" {
  let tokens = [Keyword(Use), Token::Identifier("mydb"), Token::Semicolon]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(stmt, content="USE mydb;")
}

///|
test "Parse USE database statement with qualified name" {
  let tokens = [
    Keyword(Use),
    Token::Identifier("company"),
    Token::Period,
    Token::Identifier("production"),
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(stmt, content="USE company.production;")
}

///|
test "Parse USE database statement with backtick quoted name" {
  let tokens = [
    Keyword(Use),
    Token::Identifier("my database"),
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(stmt, content="USE `my database`;")
}

///|
test "Parse COPY table TO file" {
  let tokens = [
    Keyword(Copy),
    Token::Identifier("users"),
    Keyword(To),
    Token::StringLiteral("/tmp/users.csv"),
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(stmt, content="COPY users TO '/tmp/users.csv';")
}

///|
test "Parse COPY table FROM file with CSV format" {
  let tokens = [
    Keyword(Copy),
    Token::Identifier("products"),
    Keyword(From),
    Token::StringLiteral("/data/products.csv"),
    Token::LParen,
    Keyword(Format),
    Token::Identifier("csv"),
    Token::Comma,
    Keyword(Header),
    Token::RParen,
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(
    stmt,
    content="COPY products FROM '/data/products.csv' (FORMAT CSV, HEADER);",
  )
}

///|
test "Parse COPY table with column list TO STDOUT" {
  let tokens = [
    Keyword(Copy),
    Token::Identifier("orders"),
    Token::LParen,
    Token::Identifier("id"),
    Token::Comma,
    Token::Identifier("amount"),
    Token::RParen,
    Keyword(To),
    Keyword(Stdout),
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(stmt, content="COPY orders (id, amount) TO STDOUT;")
}

///|
test "Parse COPY query TO file with options" {
  let tokens = [
    Keyword(Copy),
    Token::LParen,
    Keyword(Select),
    Token::Mul,
    Keyword(From),
    Token::Identifier("customers"),
    Token::RParen,
    Keyword(To),
    Token::StringLiteral("/tmp/customers.csv"),
    Token::LParen,
    Keyword(Format),
    Token::Identifier("csv"),
    Token::Comma,
    Token::Identifier("delimiter"),
    Token::StringLiteral(","),
    Token::Comma,
    Keyword(Null),
    Token::StringLiteral(""),
    Token::RParen,
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(
    stmt,
    content="COPY (\n  SELECT\n    *\n  FROM\n    customers\n) TO '/tmp/customers.csv' (FORMAT CSV, DELIMITER ',', NULL '');",
  )
}

///|
test "Parse COPY FROM STDIN with comprehensive options" {
  let tokens = [
    Keyword(Copy),
    Token::Identifier("employees"),
    Keyword(From),
    Keyword(Stdin),
    Token::LParen,
    Keyword(Format),
    Token::Identifier("csv"),
    Token::Comma,
    Token::Identifier("quote"),
    Token::StringLiteral("\""),
    Token::Comma,
    Token::Identifier("escape"),
    Token::StringLiteral("\\"),
    Token::Comma,
    Keyword(ForceQuote),
    Token::Mul,
    Token::RParen,
    Token::Semicolon,
  ]
  let stmt = parse(tokens)[0] |> pretty_print
  inspect(
    stmt,
    content="COPY employees FROM STDIN (FORMAT CSV, QUOTE '\"', ESCAPE '\\', FORCE_QUOTE *);",
  )
}

///|
fn Parser::parse_duplicate_key_assignments(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[Array[Assignment]] raise ParserError {
  let assignments = []
  let tokens = loop tokens {
    [Identifier(column), Eq, .. tokens] => {
      let (value, tokens) = self.parse_expr(tokens)
      assignments.push({ column, value })
      match tokens {
        [Comma, .. tokens] => continue tokens
        _ => break tokens
      }
    }
    [token, .. _tokens] =>
      raise UnexpectedTokenMessageError(
        token, "expected column assignment in ON DUPLICATE KEY UPDATE",
      )
    [] =>
      raise InternalBug(
        "parse_duplicate_key_assignments: unexpected end of tokens",
      )
  }
  (assignments, tokens)
}

///|
fn Parser::parse_on_conflict_clause(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[OnConflictClause] raise ParserError {
  // Parse optional conflict target
  let (conflict_target, tokens) = match tokens {
    [LParen, .. tokens] => {
      // Parse column list or expression
      let (first_expr, tokens) = self.parse_expr(tokens)

      // Check if it's a simple column list or expression
      match tokens {
        [Comma, .. tokens] => {
          // It's a column list: (col1, col2, ...)
          let columns = []
          // Extract column name from first expression if it's an identifier
          match first_expr {
            Expr::Identifier(name) => columns.push(name)
            _ =>
              raise UnexpectedTokenMessageError(
                tokens[0],
                "expected column name in conflict target",
              )
          }
          let tokens = loop tokens {
            [Identifier(col), Comma, .. tokens] => {
              columns.push(col)
              continue tokens
            }
            [Identifier(col), RParen, .. tokens] => {
              columns.push(col)
              break tokens
            }
            [token, ..] =>
              raise UnexpectedTokenMessageError(token, "expected column name")
            [] =>
              raise InternalBug(
                "parse_on_conflict_clause: unexpected end in column list",
              )
          }

          // Check for optional WHERE condition after parentheses
          let (where_condition, tokens) = match tokens {
            [Keyword(Where), .. tokens] => {
              let (condition, tokens) = self.parse_expr(tokens)
              (Some(condition), tokens)
            }
            _ => (None, tokens)
          }

          // For multiple columns, use Columns variant if no WHERE condition, otherwise OnExpression
          match where_condition {
            None => (Some(ConflictTarget::Columns(columns)), tokens)
            Some(_condition) =>
              (
                Some(
                  ConflictTarget::OnExpression(
                    Expr::CompoundIdentifier(columns),
                    where_condition,
                  ),
                ),
                tokens,
              )
          }
        }
        [RParen, .. tokens] => {
          // Single expression or column

          // Check for optional WHERE condition after parentheses
          let (where_condition, tokens) = match tokens {
            [Keyword(Where), .. tokens] => {
              let (condition, tokens) = self.parse_expr(tokens)
              (Some(condition), tokens)
            }
            _ => (None, tokens)
          }
          match first_expr {
            Expr::Identifier(name) =>
              // Single column case
              match where_condition {
                None => (Some(ConflictTarget::Columns([name])), tokens)
                Some(_) =>
                  (
                    Some(
                      ConflictTarget::OnExpression(first_expr, where_condition),
                    ),
                    tokens,
                  )
              }
            _ =>
              (
                Some(ConflictTarget::OnExpression(first_expr, where_condition)),
                tokens,
              )
          }
        }
        [token, ..] => raise UnexpectedTokenError(token, RParen)
        [] =>
          raise InternalBug(
            "parse_on_conflict_clause: unexpected end after expression",
          )
      }
    }
    [Keyword(On), Keyword(Constraint), Identifier(constraint_name), .. tokens] =>
      (Some(ConflictTarget::OnConstraint(constraint_name)), tokens)
    _ => (None, tokens)
  }

  // Parse conflict action (DO NOTHING or DO UPDATE SET ...)
  let (conflict_action, tokens) = match tokens {
    [Keyword(Do), Keyword(Nothing), .. tokens] =>
      (ConflictAction::DoNothing, tokens)
    [Keyword(Do), Keyword(Update), Keyword(Set), .. tokens] => {
      let (assignments, tokens) = self.parse_duplicate_key_assignments(tokens)

      // Parse optional WHERE clause for DO UPDATE
      let (where_condition, tokens) = match tokens {
        [Keyword(Where), .. tokens] => {
          let (condition, tokens) = self.parse_expr(tokens)
          (Some(condition), tokens)
        }
        _ => (None, tokens)
      }
      (ConflictAction::DoUpdate(assignments, where_condition), tokens)
    }
    [token, ..] =>
      raise UnexpectedTokenMessageError(
        token, "expected DO NOTHING or DO UPDATE SET",
      )
    [] =>
      raise InternalBug("parse_on_conflict_clause: expected conflict action")
  }
  ({ conflict_target, conflict_action }, tokens)
}

///|
fn Parser::parse_load_data_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[LoadDataStmt] raise ParserError {
  let tokens = self.expect_token(tokens, Keyword(Load))
  let tokens = self.expect_token(tokens, Keyword(Data))

  // Parse optional LOCAL keyword
  let (is_local, tokens) = match tokens {
    [Keyword(Local), .. tokens] => (true, tokens)
    _ => (false, tokens)
  }

  // Parse INFILE 'filename'
  let tokens = self.expect_token(tokens, Keyword(Infile))
  let (filename, tokens) = match tokens {
    [StringLiteral(filename), .. tokens] => (filename, tokens)
    [token, ..] =>
      raise UnexpectedTokenMessageError(
        token, "expected filename string after INFILE",
      )
    [] =>
      raise InternalBug(
        "parse_load_data_statement: unexpected end after INFILE",
      )
  }

  // Parse optional REPLACE or IGNORE
  let (duplicate_handling, tokens) = match tokens {
    [Keyword(Replace), .. tokens] =>
      (Some(LoadDataDuplicateHandling::Replace), tokens)
    [Keyword(Ignore), .. tokens] =>
      (Some(LoadDataDuplicateHandling::Ignore), tokens)
    _ => (None, tokens)
  }

  // Parse INTO TABLE table_name
  let tokens = self.expect_token(tokens, Keyword(Into))
  let tokens = self.expect_token(tokens, Keyword(Table))
  let (table_name, tokens) = self.parse_object_name(tokens)

  // Parse optional CHARACTER SET
  let (character_set, tokens) = match tokens {
    [Keyword(Character), Keyword(Set), Identifier(charset), .. tokens] =>
      (Some(charset), tokens)
    _ => (None, tokens)
  }

  // Parse optional FIELDS clause
  let (fields_options, tokens) = match tokens {
    [Keyword(Fields), .. tokens] => {
      let (fields_opts, tokens) = self.parse_load_data_fields_options(tokens)
      (Some(fields_opts), tokens)
    }
    _ => (None, tokens)
  }

  // Parse optional LINES clause
  let (lines_options, tokens) = match tokens {
    [Keyword(Lines), .. tokens] => {
      let (lines_opts, tokens) = self.parse_load_data_lines_options(tokens)
      (Some(lines_opts), tokens)
    }
    _ => (None, tokens)
  }

  // Parse optional IGNORE number LINES
  let (ignore_lines, tokens) = match tokens {
    [Keyword(Ignore), Number(num_str), Keyword(Lines), .. tokens] => {
      // Convert string to integer
      let num = @string.parse_int(num_str) catch {
        _ =>
          raise UnexpectedTokenMessageError(
            tokens[0],
            "expected valid number for IGNORE lines",
          )
      }
      (Some(num), tokens)
    }
    _ => (None, tokens)
  }

  // Parse optional column list: (col1, col2, ...)
  let (columns, tokens) = match tokens {
    [LParen, .. tokens] => {
      let columns = []
      let tokens = loop tokens {
        [Identifier(col), Comma, .. tokens] => {
          columns.push(col)
          continue tokens
        }
        [Identifier(col), RParen, .. tokens] => {
          columns.push(col)
          break tokens
        }
        // Allow keywords to be used as column names
        [Keyword(Date), Comma, .. tokens] => {
          columns.push("date")
          continue tokens
        }
        [Keyword(Date), RParen, .. tokens] => {
          columns.push("date")
          break tokens
        }
        [Keyword(Year), Comma, .. tokens] => {
          columns.push("year")
          continue tokens
        }
        [Keyword(Year), RParen, .. tokens] => {
          columns.push("year")
          break tokens
        }
        [Keyword(Month), Comma, .. tokens] => {
          columns.push("month")
          continue tokens
        }
        [Keyword(Month), RParen, .. tokens] => {
          columns.push("month")
          break tokens
        }
        [Keyword(Day), Comma, .. tokens] => {
          columns.push("day")
          continue tokens
        }
        [Keyword(Day), RParen, .. tokens] => {
          columns.push("day")
          break tokens
        }
        [token, ..] =>
          raise UnexpectedTokenMessageError(
            token, "expected column name in LOAD DATA column list",
          )
        [] =>
          raise InternalBug(
            "parse_load_data_statement: unexpected end in column list",
          )
      }
      (Some(columns), tokens)
    }
    _ => (None, tokens)
  }

  // Parse optional SET assignments
  let (set_assignments, tokens) = match tokens {
    [Keyword(Set), .. tokens] => {
      let (assignments, tokens) = self.parse_duplicate_key_assignments(tokens)
      (Some(assignments), tokens)
    }
    _ => (None, tokens)
  }
  (
    {
      is_local,
      filename,
      duplicate_handling,
      table_name,
      character_set,
      fields_options,
      lines_options,
      ignore_lines,
      columns,
      set_assignments,
    },
    tokens,
  )
}

///|
fn Parser::parse_load_data_fields_options(
  _self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[LoadDataFieldsOptions] {
  let mut terminated_by = None
  let mut enclosed_by = None
  let mut optionally_enclosed = false
  let mut escaped_by = None
  let tokens = loop tokens {
    [Keyword(Terminated), Keyword(By), StringLiteral(term), .. tokens] => {
      terminated_by = Some(term)
      continue tokens
    }
    [
      Keyword(Optionally),
      Keyword(Enclosed),
      Keyword(By),
      StringLiteral(enc),
      .. tokens,
    ] => {
      enclosed_by = Some(enc)
      optionally_enclosed = true
      continue tokens
    }
    [Keyword(Enclosed), Keyword(By), StringLiteral(enc), .. tokens] => {
      enclosed_by = Some(enc)
      optionally_enclosed = false
      continue tokens
    }
    [Keyword(Escaped), Keyword(By), StringLiteral(esc), .. tokens] => {
      escaped_by = Some(esc)
      continue tokens
    }
    tokens => break tokens
  }
  ({ terminated_by, enclosed_by, optionally_enclosed, escaped_by }, tokens)
}

///|
fn Parser::parse_load_data_lines_options(
  _self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[LoadDataLinesOptions] {
  let mut starting_by = None
  let mut terminated_by = None
  let tokens = loop tokens {
    [Keyword(Starting), Keyword(By), StringLiteral(start), .. tokens] => {
      starting_by = Some(start)
      continue tokens
    }
    [Keyword(Terminated), Keyword(By), StringLiteral(term), .. tokens] => {
      terminated_by = Some(term)
      continue tokens
    }
    tokens => break tokens
  }
  ({ starting_by, terminated_by }, tokens)
}

///|
/// Parse PREPARE statement
/// Syntax: PREPARE name [(data_type [, ...])] AS statement
fn Parser::parse_prepare_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[PrepareStmt] raise ParserError {
  // PREPARE
  let tokens = self.expect_token(tokens, Keyword(Prepare))

  // name
  let (name, tokens) = match tokens {
    [Identifier(name), .. tokens] => (name, tokens)
    _ => raise UnexpectedTokenMessageError(tokens[0], "prepared statement name")
  }

  // Optional data types: [(data_type, ...)]
  let (data_types, tokens) = match tokens {
    [LParen, .. rest_tokens] => {
      let data_types = []
      let tokens = loop rest_tokens {
        [RParen, .. rest_tokens] => break rest_tokens
        rest_tokens => {
          let (data_type, rest_tokens) = self.parse_data_type(rest_tokens)
          data_types.push(data_type)
          match rest_tokens {
            [Comma, .. rest_tokens] => continue rest_tokens
            [RParen, .. rest_tokens] => break rest_tokens
            [token, ..] => raise UnexpectedTokenError(token, RParen)
            [] =>
              raise ParserError::InternalBug(
                "parse_prepare_statement: unexpected end of tokens",
              )
          }
        }
      }
      (data_types, tokens)
    }
    _ => ([], tokens)
  }

  // AS
  let tokens = self.expect_token(tokens, Keyword(As))

  // statement
  let (statement, tokens) = self.parse_statement(tokens)
  ({ name, data_types, statement }, tokens)
}

///|
/// Parse EXECUTE statement
/// Syntax:
///   EXECUTE name [(param [, ...])]
///   EXECUTE name USING expr [, ...]
///   EXECUTE IMMEDIATE 'sql_string' [INTO var [, ...]] [USING expr [, ...]]
fn Parser::parse_execute_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[ExecuteStmt] raise ParserError {
  // EXECUTE
  let tokens = self.expect_token(tokens, Keyword(Execute))

  // Check for IMMEDIATE (EXECUTE IMMEDIATE is different from EXECUTE name)
  let (name, tokens) = match tokens {
    [Keyword(Immediate), .. tokens] => (None, tokens)
    [Identifier(id), .. tokens] => {
      let object_name : ObjectName = { parts: [id] }
      (Some(object_name), tokens)
    }
    _ =>
      raise UnexpectedTokenMessageError(
        tokens[0],
        "statement name or IMMEDIATE",
      )
  }

  // Parameters: either (param1, param2, ...) or USING expr1, expr2, ...
  let (parameters, has_parentheses, tokens) = match tokens {
    [LParen, .. rest_tokens] => {
      let parameters = []
      let tokens = loop rest_tokens {
        [RParen, .. rest_tokens] => break rest_tokens
        rest_tokens => {
          let (expr, rest_tokens) = self.parse_expr(rest_tokens)
          parameters.push(expr)
          match rest_tokens {
            [Comma, .. rest_tokens] => continue rest_tokens
            [RParen, .. rest_tokens] => break rest_tokens
            [token, ..] => raise UnexpectedTokenError(token, RParen)
            [] =>
              raise ParserError::InternalBug(
                "parse_execute_statement: unexpected end of tokens",
              )
          }
        }
      }
      (parameters, true, tokens)
    }
    _ => ([], false, tokens)
  }

  // INTO clause (Oracle style): INTO var1, var2, ...
  let (into, tokens) = match tokens {
    [Keyword(Into), .. rest_tokens] => {
      let into = []
      let first = match rest_tokens {
        [Identifier(id), .. tokens] => {
          into.push(id)
          tokens
        }
        _ => rest_tokens
      }
      let tokens = loop first {
        [Comma, Identifier(id), .. tokens] => {
          into.push(id)
          continue tokens
        }
        tokens => break tokens
      }
      (into, tokens)
    }
    _ => ([], tokens)
  }

  // USING clause: USING expr1, expr2, ...
  let (using_exprs, tokens) = match tokens {
    [Keyword(Using), .. rest_tokens] => {
      let using_exprs = []
      let (expr, rest_tokens) = self.parse_expr(rest_tokens)
      using_exprs.push(expr)
      let tokens = loop rest_tokens {
        [Comma, .. rest_tokens] => {
          let (expr, rest_tokens) = self.parse_expr(rest_tokens)
          using_exprs.push(expr)
          continue rest_tokens
        }
        tokens => break tokens
      }
      (using_exprs, tokens)
    }
    _ => ([], tokens)
  }
  ({ name, parameters, has_parentheses, into, using_exprs }, tokens)
}

///|
/// Parse DEALLOCATE statement
/// Syntax: DEALLOCATE [PREPARE] name
fn Parser::parse_deallocate_statement(
  self : Parser,
  tokens : ArrayView[Token],
) -> ParserResult[DeallocateStmt] raise ParserError {
  // DEALLOCATE
  let tokens = self.expect_token(tokens, Keyword(Deallocate))

  // Optional PREPARE keyword
  let (prepare, tokens) = match tokens {
    [Keyword(Prepare), .. tokens] => (true, tokens)
    _ => (false, tokens)
  }

  // name
  let (name, tokens) = match tokens {
    [Identifier(name), .. tokens] => (name, tokens)
    _ => raise UnexpectedTokenMessageError(tokens[0], "prepared statement name")
  }
  ({ name, prepare }, tokens)
}