// 公共 API 收口:`zxcvbn()` 入口与结果类型。
//
// 移植来源:dropbox/zxcvbn `src/main.coffee` + `time_estimates.coffee` + `feedback.coffee`
// (MIT, (c) Dropbox, Inc.)
//
// 匹配/评分的实现分别在 matching.mbt / scoring.mbt;这里只做编排。

///|
/// 四类破解场景的耗时(秒)。
///
/// 对应上游的四档攻击模型:在线限流(100 次/小时)、在线不限流(10 次/秒)、
/// 离线慢哈希(1e4 次/秒)、离线快哈希(1e10 次/秒)。
pub struct CrackTimes {
  online_throttling_100_per_hour : Double
  online_no_throttling_10_per_second : Double
  offline_slow_hashing_1e4_per_second : Double
  offline_fast_hashing_1e10_per_second : Double
} derive(Eq, Debug)

///|
pub extend CrackTimes with Eq::{equal, not_equal}

///|
pub extend CrackTimes with @debug.Debug::{to_repr}

///|
/// 可执行的改进建议(与上游 feedback 对齐)。
pub struct Feedback {
  warning : String
  suggestions : Array[String]
} derive(Eq, Debug)

///|
pub extend Feedback with Eq::{equal, not_equal}

///|
pub extend Feedback with @debug.Debug::{to_repr}

///|
/// zxcvbn 评估结果。
///
/// `guesses` / `guesses_log10` / `score` 是保守的**攻击模型下限**估计:
/// zxcvbn 不是"字符类别计数",而是做完全模式匹配后取最小猜测数。
pub struct Entropy {
  password : String
  /// 保守估计的猜测次数
  guesses : Double
  /// log10(guesses)
  guesses_log10 : Double
  /// 0..4:阈值 1e3 / 1e6 / 1e8 / 1e10(与上游一致)
  score : Int
  /// 四场景破解秒数
  crack_times_seconds : CrackTimes
  /// 人类可读破解时间
  crack_times_display : CrackTimesDisplay
  /// 取最小 guesses 的最优非重叠匹配序列
  sequence : Array[Match]
  feedback : Feedback
} derive(Eq, Debug)

///|
pub extend Entropy with Eq::{equal, not_equal}

///|
pub extend Entropy with @debug.Debug::{to_repr}

///|
/// 上游 `feedback.default_feedback`:没有任何可用建议时的兜底。
pub let default_feedback : Feedback = {
  warning: "",
  suggestions: [
    "Use a few words, avoid common phrases", "No need for symbols, digits, or uppercase letters",
  ],
}

///|
/// 评估一个密码的强度。
///
/// - `user_inputs`:与用户相关的词(用户名/邮箱等),命中时显著降低强度估计;
/// - `reference_year`:日期匹配的参考年份。上游取运行时当年,MoonBit core 没有
///   时钟 API,故参数化并默认 2026,同时让测试完全确定。
///
/// 移植来源:dropbox/zxcvbn(MIT)。
pub fn zxcvbn(
  password : String,
  user_inputs : Array[String],
  reference_year? : Int = 2026,
) -> Entropy {
  // 上游用模块级全局变量 `set_user_input_dictionary` 传递用户输入,
  // 每次调用都要重置以保持无状态;这里改为调用时传入,天然纯函数、可重入。
  let sanitized = sanitize_user_inputs(user_inputs)
  let user_dict = build_ranked_dict(sanitized)
  let matcher = Omnimatcher::new(user_dict, reference_year)
  let matches = matcher.run(password)
  let result = most_guessable_match_sequence(password, matches, reference_year)
  let attack = estimate_attack_times(result.guesses)
  let feedback = get_feedback(attack.score, result.sequence)
  {
    password,
    guesses: result.guesses,
    guesses_log10: result.guesses_log10,
    score: attack.score,
    crack_times_seconds: attack.crack_times_seconds,
    crack_times_display: attack.crack_times_display,
    sequence: result.sequence,
    feedback,
  }
}

///|
/// 上游 `main.coffee` 的 `sanitized_inputs`:只接受字符串(MoonBit 侧本来就是
/// `Array[String]`)并统一小写化。
fn sanitize_user_inputs(user_inputs : Array[String]) -> Array[String] {
  user_inputs.map(input => input.to_lower())
}

///|
/// 便捷入口:不带用户输入、用默认参考年。
pub fn zxcvbn_password(password : String) -> Entropy {
  zxcvbn(password, [])
}