// Copyright 2026 PaiGack
// Licensed under the Apache License, Version 2.0.
// Ported from jlaffaye/ftp (ISC License), see LICENSE-THIRD-PARTY.

// Layer: pure logic — no IO, no `moonbitlang/async` dependency.

// RFC 959 / RFC 2228 / RFC 2428 / RFC 3659 status codes and the constants
// the client shares with them.
//
// This file merges `status` and `consts`: both are plain protocol constants
// (status codes and the dial timeout), they never changed apart, and keeping
// them together makes the "where do I find the protocol constants" question
// have one answer.

///|
/// RFC 959 / RFC 2228 / RFC 2428 / RFC 3659 status codes.
///
/// Values are kept byte-for-byte identical to upstream `status.go`. Several
/// distinct constants intentionally share the same numeric value (the RFCs
/// overload the code), which is fine because they are plain `Int` constants.
pub let status_restart_marker_answer : Int = 110

///|
pub let status_restart_marker_reply : Int = 120

///|
pub let status_data_conn_already_in_use : Int = 125

///|
/// `221 Service closing control connection.` — the answer to `QUIT`.
pub let status_closing_control_connection : Int = 221

///|
pub let status_closing_data_connection : Int = 226

///|
pub let status_auth_ok : Int = 234

///|
pub let status_file_action_ok : Int = 250

///|
pub let status_dir_create : Int = 257

///|
pub let status_user_logged_in_proceed : Int = 230

///|
pub let status_username_ok_need_password : Int = 331

///|
pub let status_file_action_pending_further_info : Int = 350

///|
pub let status_service_ready_for_new_user : Int = 220

///|
pub let status_session_opened_data_connection : Int = 225

///|
pub let status_data_connection_open_no_transfer : Int = 200

///|
pub let status_cmd_ok : Int = 200

///|
pub let status_cmd_not_implemented_superfluous : Int = 202

///|
pub let status_system_status : Int = 211

///|
pub let status_dir_status : Int = 212

///|
pub let status_file_status : Int = 213

///|
pub let status_help_message : Int = 214

///|
pub let status_name_status : Int = 215

///|
pub let status_service_ready_soon : Int = 120

///|
pub let status_data_connection_already_open : Int = 125

///|
pub let status_enter_passive_mode : Int = 227

///|
pub let status_enter_extended_passive_mode : Int = 229

///|
pub let status_entering_passive_mode_from_ip : Int = 227

///|
pub let status_enter_port : Int = 200

///|
pub let status_enter_extended_port : Int = 200

///|
pub let status_username_ok : Int = 331

///|
pub let status_need_account_for_login : Int = 332

///|
pub let status_file_action_not_taken : Int = 450

///|
pub let status_file_action_aborted : Int = 550

///|
pub let status_restart_marker_not_understood : Int = 501

///|
pub let status_service_unavailable : Int = 421

///|
pub let status_cannot_open_data_connection : Int = 425

///|
pub let status_transfer_aborted : Int = 426

///|
pub let status_file_action_aborted_local_error : Int = 451

///|
pub let status_syntax_error_unknown_cmd : Int = 500

///|
pub let status_syntax_error_unknown_params : Int = 501

///|
pub let status_not_implemented : Int = 502

///|
pub let status_not_implemented_for_param : Int = 504

///|
pub let status_not_logged_in : Int = 530

///|
pub let status_need_account : Int = 532

///|
pub let status_request_denied : Int = 550

///|
pub let status_page_type_unknown : Int = 551

///|
pub let status_exceeded_storage_allocation : Int = 552

///|
pub let status_action_not_taken : Int = 450

///|
pub let status_bad_filename : Int = 553

///|
/// The human readable text for every known status code, copied from
/// upstream `status.go` (no localization on purpose).
pub fn status_text(code : Int) -> String {
  match code {
    110 => "Restart marker reply."
    120 => "Service ready in 2 minutes."
    125 => "Data connection already open, transfer starting."
    150 => "File status okay, about to open data connection."
    200 => "Command okay."
    202 => "Command not implemented, superfluous at this site."
    211 => "System status."
    212 => "Directory status."
    213 => "File status."
    214 => "Help message."
    215 => "NAME system type."
    220 => "Service ready for new user."
    221 => "Service closing control connection."
    225 => "Data connection open, no transfer in progress."
    226 => "Closing data connection."
    227 => "Entering Passive Mode."
    229 => "Entering Extended Passive Mode."
    230 => "User logged in, proceed."
    234 => "Authentication successful."
    250 => "Requested file action okay, completed."
    257 => "PATHNAME created."
    331 => "Username okay, need password."
    332 => "Need account for login."
    350 => "Requested file action pending further information."
    421 => "Service not available, closing control connection."
    425 => "Can't open data connection."
    426 => "Connection closed, transfer aborted."
    430 => "Invalid username or password."
    450 => "Requested file action not taken."
    451 => "Requested action aborted, local error in processing."
    452 => "Requested action not taken, insufficient storage space."
    500 => "Syntax error, command unrecognized."
    501 => "Syntax error in parameters or arguments."
    502 => "Command not implemented."
    503 => "Bad sequence of commands."
    504 => "Command not implemented for that parameter."
    530 => "Not logged in."
    532 => "Need account for storing files."
    550 => "Requested action not taken."
    551 => "Requested action aborted, page type unknown."
    552 => "Requested file action aborted, exceeded storage allocation."
    553 => "Requested action not taken, file name not allowed."
    _ => "Unknown status code: \{code}"
  }
}

///|
/// Whether the code is a 2xx "positive completion" reply.
pub fn is_positive_completion(code : Int) -> Bool {
  code >= 200 && code < 300
}

///|
/// Whether the code is a 1xx "positive intermediate" reply.
pub fn is_positive_intermediate(code : Int) -> Bool {
  code >= 100 && code < 200
}

///|
/// Default timeout used when dialing a server, in milliseconds
/// (upstream `ftp.DefaultDialTimeout`, 30s).
pub let default_dial_timeout_ms : Int = 30 * 1000