///|
/// 认证域。
///
/// 两种认证方式都由 core 的 `Client` 统一处理:
///
/// - 直接给永久 token(`create_open_list_client_with_token`)——此时
///   `login` 不必要,`is_logged_in()` 一开始就是 `true`;
/// - 给账号密码(`Credentials::password`)——客户端在第一个需要认证的请求
///   前自动登录换限时 token,token 过期(401)时自动重登一次。
///
/// 因此业务代码**不需要**自己调 `login`;只在「想立刻验证密码是否正确」或
/// 「想自己控制登录时机」时才手动调用,`login` 会顺手把 token 存进共享状态。
pub struct Auth {
  priv client : @core.Client
}

///|
/// 由根包创建(每个域模块的构造器都是这样,使用者拿不到伪造状态的机会)。
pub fn Auth::new(client : @core.Client) -> Auth {
  { client, }
}

///|
/// 用账号密码登录(`POST /api/auth/login`)。
///
/// 服务端会对请求里的密码做静态哈希后再校验,所以这里传**明文**密码。
/// 返回并记住换来的限时 token。常见失败:401 用户名或密码错误、
/// 402 两步验证码错误、429 失败次数过多(同一 IP 5 次后锁 5 分钟)。
pub async fn Auth::login(
  self : Auth,
  username : String,
  password : String,
  otp_code? : String,
) -> String raise @core.OpenListError {
  let token = self.request_token(
    "/api/auth/login", username, password, otp_code,
  )
  self.client.set_token(Some(token))
  token
}

///|
/// 用「前端已做静态哈希」的密码登录(`POST /api/auth/login/hash`)。
///
/// 服务端不会再哈希,因此 `hashed_password` 必须是 `SHA256(密码 + "-" + salt)`
/// 形式的结果;本模块**不**实现哈希算法,需要的话在调用方算好再传进来。
pub async fn Auth::login_hash(
  self : Auth,
  username : String,
  hashed_password : String,
  otp_code? : String,
) -> String raise @core.OpenListError {
  let token = self.request_token(
    "/api/auth/login/hash", username, hashed_password, otp_code,
  )
  self.client.set_token(Some(token))
  token
}

///|
/// 用 LDAP 账号登录(`POST /api/auth/login/ldap`)。
pub async fn Auth::login_ldap(
  self : Auth,
  username : String,
  password : String,
  otp_code? : String,
) -> String raise @core.OpenListError {
  let token = self.request_token(
    "/api/auth/login/ldap", username, password, otp_code,
  )
  self.client.set_token(Some(token))
  token
}

///|
/// 三个登录端点共用的请求逻辑。
async fn Auth::request_token(
  self : Auth,
  path : String,
  username : String,
  password : String,
  otp_code : String?,
) -> String raise @core.OpenListError {
  let body = LoginBody::{ username, password, otp_code, }
  let data = self.client.request(
    @moonhttp.Method::Post,
    path,
    body=@json.to_json(body),
  )
  decode_token(data)
}

///|
/// 退出登录(`GET /api/auth/logout`):让服务端作废当前 token,并清空本地状态。
///
/// 本来就没登录时直接返回,不发请求。服务端即使报错(例如 token 已失效)
/// 本地状态同样会被清空——退出登录不该因为网络问题而失败在半途。
pub async fn Auth::logout(self : Auth) -> Unit raise @core.OpenListError {
  if !self.client.is_logged_in() {
    return
  }
  errdefer self.client.set_token(None)
  let _ = self.client.request(@moonhttp.Method::Get, "/api/auth/logout")
  self.client.set_token(None)
}

///|
/// 当前 token;永久 token 一开始就有,账号密码凭证则是登录后才有。
pub fn Auth::token(self : Auth) -> String? {
  self.client.token()
}

///|
/// 是否已持有 token。
pub fn Auth::is_logged_in(self : Auth) -> Bool {
  self.client.is_logged_in()
}

///|
/// 直接设置 token(从外部持久化状态恢复会话,或主动作废)。
pub fn Auth::set_token(self : Auth, token : String?) -> Unit {
  self.client.set_token(token)
}

///|
/// 生成两步验证密钥与二维码(`POST /api/auth/2fa/generate`)。
///
/// 需要已登录。真正的开启动作是随后调用 `verify_2fa`。
pub async fn Auth::generate_2fa(
  self : Auth,
) -> TwoFactorAuth raise @core.OpenListError {
  let data = self.client.request(
    @moonhttp.Method::Post,
    "/api/auth/2fa/generate",
  )
  @core.decode_data(data)
}

///|
/// 用验证器上的 6 位码开启两步验证(`POST /api/auth/2fa/verify`)。
pub async fn Auth::verify_2fa(
  self : Auth,
  code : String,
  secret : String,
) -> Unit raise @core.OpenListError {
  let body = VerifyTwoFactorRequest::{ code, secret, }
  let _ = self.client.request(
    @moonhttp.Method::Post,
    "/api/auth/2fa/verify",
    body=@json.to_json(body),
  )
}