// token.mbt — token 核:签发/校验(定序 JSON 载荷 + base64url 两段 + mac)

///|
/// 签发产物:线形 token(payload.mac 两段)+ 定型 claims。
pub(all) struct Issued {
  token : String
  claims : Claims
} derive(Eq, Debug)

///|
/// 校验错误:mac 之后的错携带 claims(签名已过,载荷可信,供审计定罪);
/// mac 之前的错不携带(不可信物不外传)。
pub(all) enum VerifyErr {
  Malformed(String)
  BadMac
  Expired(Claims)
  NotYetValid(Claims)
  WrongIssuer(Claims)
  WrongAudience(Claims)
  WrongType(Claims)
} derive(Eq, Debug)

///|
/// 签发:claims 定型 → 定序载荷 JSON → mac → base64url 两段线形。
/// 纯函数:时间(now)与熵(jti)全由参数注入,核心零时钟零随机(D-G2)。
pub fn issue(
  config : GuardConfig,
  signer : &Signer,
  now : Int64,
  sub : String,
  typ : TokenType,
  jti : String,
) -> Issued {
  let ttl = match typ {
    TokenType::Access => config.access_ttl_ms
    TokenType::Refresh => config.refresh_ttl_ms
  }
  let claims : Claims = {
    sub,
    iss: config.issuer,
    aud: config.audience,
    iat: now,
    exp: now + ttl,
    typ,
    jti,
  }
  let payload = claims_wire(claims)
  let mac = signer.sign(payload)
  { token: b64url_encode(payload) + "." + b64url_encode(mac), claims, }
}

///|
/// 校验:分段 → 解码 → mac 核(先于解析,不碰未信物)→ 定型 →
/// iss/aud → 时间窗(漂移界)。管线 verify 段的核(四段 runner G2 组)。
pub fn verify(
  config : GuardConfig,
  signer : &Signer,
  now : Int64,
  token : String,
) -> Result[Claims, VerifyErr] {
  let (payload_seg, mac_seg) = match split_dot(token) {
    Some(pair) => pair
    None =>
      return Err(VerifyErr::Malformed("需两段 base64url 以 '.' 分隔"))
  }
  let payload = match b64url_decode(payload_seg) {
    Some(b) => b
    None => return Err(VerifyErr::Malformed("载荷段非 base64url"))
  }
  let mac = match b64url_decode(mac_seg) {
    Some(b) => b
    None => return Err(VerifyErr::Malformed("mac 段非 base64url"))
  }
  if signer.verify(payload, mac) == false {
    return Err(VerifyErr::BadMac)
  }
  let claims = match claims_of_wire(payload) {
    Some(c) => c
    None => return Err(VerifyErr::Malformed("载荷非定型 claims"))
  }
  if claims.iss != config.issuer {
    return Err(VerifyErr::WrongIssuer(claims))
  }
  if claims.aud != config.audience {
    return Err(VerifyErr::WrongAudience(claims))
  }
  if now > claims.exp + config.allowed_drift_ms {
    return Err(VerifyErr::Expired(claims))
  }
  if claims.iat > now + config.allowed_drift_ms {
    return Err(VerifyErr::NotYetValid(claims))
  }
  Ok(claims)
}

///|
/// 带门校验:typ 不对即 WrongType(门面用——access 门不收 refresh)。
pub fn verify_for(
  config : GuardConfig,
  signer : &Signer,
  now : Int64,
  token : String,
  typ : TokenType,
) -> Result[Claims, VerifyErr] {
  let claims = match verify(config, signer, now, token) {
    Ok(c) => c
    Err(e) => return Err(e)
  }
  if claims.typ != typ {
    return Err(VerifyErr::WrongType(claims))
  }
  Ok(claims)
}

///|
/// 首点分段;余点交给 b64url 字符集校验兜底('.' 不在字母表 → Malformed)。
fn split_dot(token : String) -> (String, String)? {
  let n = token.length()
  let mut dot = -1
  let mut i = 0
  while i < n {
    if token[i].to_int() == 46 {
      dot = i
      i = n
    } else {
      i += 1
    }
  }
  if dot < 0 {
    return None
  }
  Some((token[0:dot].to_owned(), token[dot + 1:n].to_owned()))
}

///|
/// 载荷定序 JSON(手写:字段序稳定,线形可读可核)。
fn claims_wire(c : Claims) -> Bytes {
  let sb = StringBuilder()
  sb.write("{\"sub\":")
  sb.write(jstr(c.sub))
  sb.write(",\"iss\":")
  sb.write(jstr(c.iss))
  sb.write(",\"aud\":")
  sb.write(jstr(c.aud))
  sb.write(",\"iat\":")
  sb.write("\{c.iat}")
  sb.write(",\"exp\":")
  sb.write("\{c.exp}")
  sb.write(",\"typ\":\"")
  sb.write(c.typ.wire())
  sb.write("\",\"jti\":")
  sb.write(jstr(c.jti))
  sb.write("}")
  @utf8.encode(sb.to_string())
}

///|
/// JSON 字符串值转义:引号/反斜杠/控制字符(\uXXXX);非 BMP 成对码元
/// 直通,孤立码元替损不致崩。
fn jstr(s : String) -> String {
  let sb = StringBuilder()
  sb.write("\"")
  let n = s.length()
  let mut i = 0
  while i < n {
    let code = s[i].to_int()
    if code == 34 {
      sb.write("\\\"")
      i += 1
    } else if code == 92 {
      sb.write("\\\\")
      i += 1
    } else if code < 32 {
      sb.write("\\u")
      sb.write(hex4(code))
      i += 1
    } else if code >= 0xD800 &&
      code < 0xDC00 &&
      i + 1 < n &&
      s[i + 1].to_int() >= 0xDC00 {
      sb.write(s[i:i + 2].to_owned())
      i += 2
    } else {
      sb.write(s[i:i + 1].to_owned())
      i += 1
    }
  }
  sb.write("\"")
  sb.to_string()
}

///|
let hex_digits : Array[String] = [
  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f",
]

///|
fn hex4(v : Int32) -> String {
  let sb = StringBuilder()
  sb.write(hex_digits[(v >> 12) & 15])
  sb.write(hex_digits[(v >> 8) & 15])
  sb.write(hex_digits[(v >> 4) & 15])
  sb.write(hex_digits[v & 15])
  sb.to_string()
}

///|
/// 定型反解:七字段全齐才认;多余字段无视(前向兼容)。
fn claims_of_wire(payload : Bytes) -> Claims? {
  let parsed = @json.parse(@utf8.decode_lossy(payload)) catch {
    _ => return None
  }
  let map = match parsed {
    Object(m) => m
    _ => return None
  }
  let sub = match jstr_field(map, "sub") {
    Some(v) => v
    None => return None
  }
  let iss = match jstr_field(map, "iss") {
    Some(v) => v
    None => return None
  }
  let aud = match jstr_field(map, "aud") {
    Some(v) => v
    None => return None
  }
  let iat = match ji64_field(map, "iat") {
    Some(v) => v
    None => return None
  }
  let exp = match ji64_field(map, "exp") {
    Some(v) => v
    None => return None
  }
  let typ = match jstr_field(map, "typ") {
    Some(w) =>
      match TokenType::of_wire(w) {
        Some(t) => t
        None => return None
      }
    None => return None
  }
  let jti = match jstr_field(map, "jti") {
    Some(v) => v
    None => return None
  }
  Some({ sub, iss, aud, iat, exp, typ, jti, })
}

///|
fn jstr_field(map : Map[String, Json], key : String) -> String? {
  match map.get(key) {
    Some(Json::String(s)) => Some(s)
    _ => None
  }
}

///|
fn ji64_field(map : Map[String, Json], key : String) -> Int64? {
  match map.get(key) {
    Some(Json::Number(d, ..)) => Some(d.to_int64())
    _ => None
  }
}

///|
/// base64url 无垫编码(JWT 式;'-'/'_' 直落,不出 '=')。
fn b64url_encode(bytes : Bytes) -> String {
  let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
  let sb = StringBuilder()
  let n = bytes.length()
  let mut i = 0
  while i < n {
    let b0 = bytes[i].to_int()
    if i + 1 >= n {
      sb.write(slice1(alphabet, (b0 >> 2) & 63))
      sb.write(slice1(alphabet, (b0 << 4) & 63))
    } else if i + 2 >= n {
      let b1 = bytes[i + 1].to_int()
      sb.write(slice1(alphabet, (b0 >> 2) & 63))
      sb.write(slice1(alphabet, ((b0 << 4) & 63) | (b1 >> 4)))
      sb.write(slice1(alphabet, (b1 << 2) & 63))
    } else {
      let b1 = bytes[i + 1].to_int()
      let b2 = bytes[i + 2].to_int()
      sb.write(slice1(alphabet, (b0 >> 2) & 63))
      sb.write(slice1(alphabet, ((b0 << 4) & 63) | (b1 >> 4)))
      sb.write(slice1(alphabet, ((b1 << 2) & 63) | (b2 >> 6)))
      sb.write(slice1(alphabet, b2 & 63))
    }
    i += 3
  }
  sb.to_string()
}

///|
/// base64url 无垫解码(严格:字母表外拒;尾残留非零拒;'=' 不收)。
fn b64url_decode(s : String) -> Bytes? {
  let n = s.length()
  if n % 4 == 1 {
    return None
  }
  let out_len = n * 6 / 8
  if out_len == 0 {
    return Some(@utf8.encode(""))
  }
  let out : Array[Byte] = Array::make(out_len, b'\x00')
  let mut acc = 0
  let mut bits = 0
  let mut written = 0
  let mut i = 0
  while i < n {
    let v = match b64url_value(s[i].to_int()) {
      Some(v) => v
      None => return None
    }
    acc = ((acc << 6) | v) & 0xFFFF
    bits += 6
    if bits >= 8 {
      bits -= 8
      out[written] = ((acc >> bits) & 255).to_byte()
      written += 1
    }
    i += 1
  }
  if bits > 0 && (acc & ((1 << bits) - 1)) != 0 {
    return None
  }
  Some(Bytes::from_array(out))
}

///|
/// ASCII 串单码元切片(调用点保证界内)。
fn slice1(s : String, i : Int32) -> String {
  s[i:i + 1].to_owned()
}

///|
fn b64url_value(code : Int32) -> Int32? {
  if code >= 65 && code <= 90 {
    Some(code - 65)
  } else if code >= 97 && code <= 122 {
    Some(code - 97 + 26)
  } else if code >= 48 && code <= 57 {
    Some(code - 48 + 52)
  } else if code == 45 {
    Some(62)
  } else if code == 95 {
    Some(63)
  } else {
    None
  }
}