/*
 * Copyright (c) 2025 SimcuTeam. All rights reserved.
 * 遵循 MIT 许可证。
 */

package simcu::simapi.helpers

import std.collection.*
import std.time.*
import std.random.*
import stdx.crypto.digest.*
import stdx.encoding.hex.*
import stdx.encoding.base64.*
import std.regex.*
import simcu::serialization.*
import simcu::simapi.communications.*
import simcu::simapi.macros.*

/**
 * 工具类。
 * 提供时间、哈希、Base64、JSON、校验等常用能力。
 */
public class SimApiUtil {
    private init() {}

    /**
     * 当前 CST 时间（UTC+8）。
     */
    public static prop cstNow: DateTime {
        get() {
            DateTime.nowUTC().addHours(8)
        }
    }

    /**
     * 当前秒级 Unix 时间戳（Double）。
     */
    public static prop timestampNow: Float64 {
        get() {
            let ts = DateTime.nowUTC().toUnixTimeStamp()
            Float64(ts / Duration.second)
        }
    }

    /**
     * SimApi 包版本号（编译期从 simapi-cj/cjpm.toml 读取。
     * 宏在调用方项目编译时展开，cwd 为应用根，故用 ../simapi-cj 相对路径）。
     */
    @ReadTomlVersion[path: "../simapi-cj/cjpm.toml"]
    public static let simApiVersion: String = ""

    /**
     * 应用版本号（编译期从调用方项目 cjpm.toml 读取）。
     */
    @ReadTomlVersion[path: "cjpm.toml"]
    public static let appVersion: String = ""

    /**
     * MD5 加密字符串。
     * @param source 源字符串。
     * @return 32 位十六进制小写。
     */
    public static func md5(source: String, mode!: String = "x2"): String {
        let md = MD5()
        md.write(source.toArray())
        formatHex(md.finish(), mode)
    }

    /**
     * SHA1 加密字符串。
     * @param source 源字符串。
     * @return 40 位十六进制小写。
     */
    public static func sha1(source: String, mode!: String = "x2"): String {
        let sha = SHA1()
        sha.write(source.toArray())
        formatHex(sha.finish(), mode)
    }

    /**
     * SHA256 加密字符串。
     * @param source 源字符串。
     * @return 64 位十六进制小写。
     */
    public static func sha256(source: String): String {
        let sha = SHA256()
        sha.write(source.toArray())
        toHexString(sha.finish())
    }

    /// 按 Md5/Sha1 的 mode 格式化：x2/x3/x4 → 每个字节 2/3/4 位十六进制
    private static func formatHex(bytes: Array<Byte>, mode: String): String {
        if (mode == "x2") {
            return toHexString(bytes)
        }
        let two = toHexString(bytes)
        var sb = StringBuilder()
        for (i in 0..bytes.size) {
            let pair = two[i * 2..i * 2 + 2]
            if (mode == "x3") {
                sb.append("0")
                sb.append(pair)
            } else if (mode == "x4") {
                sb.append("00")
                sb.append(pair)
            } else {
                sb.append(pair)
            }
        }
        sb.toString()
    }

    /**
     * 字符串 Base64 编码。
     */
    public static func base64Encode(str: String): String {
        toBase64String(str.toArray())
    }

    /**
     * 从 Base64 解码字符串。
     */
    public static func base64Decode(base64Str: String): String {
        let decoded = fromBase64String(base64Str).getOrThrow { Exception("Base64 解码失败") }
        String.fromUtf8(decoded)
    }

    /**
     * 检测手机号是否正确（中国大陆 11 位手机号）。
     */
    public static func checkCell(cell: String): Bool {
        Regex("^1[3456789]\\d{9}$").matches(cell)
    }

    /**
     * 判断是否是 Email 地址。
     * 说明：原版使用 System.Net.Mail.MailAddress 校验，仓颉无等价 API，此处用正则近似。
     */
    public static func checkEmail(email: String): Bool {
        Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$").matches(email)
    }

    /**
     * 生成 UUID v4 字符串。
     * 仓颉标准库没有 GUID 生成器（stdx 的 GUID 是 stdx.net.http 内部类型），
     * 此处用随机数自行构造：16 字节随机数 + 版本位（4）+ 变体位（10）。
     */
    public static func newGuid(): String {
        let rnd = Random()
        var bytes = Array<UInt8>(16, repeat: 0u8)
        let h = rnd.nextUInt64()
        let l = rnd.nextUInt64()
        for (i in 0..8) {
            bytes[i] = UInt8((h >> UInt64(i * 8)) & 0xFFu64)
            bytes[8 + i] = UInt8((l >> UInt64(i * 8)) & 0xFFu64)
        }
        // UUID v4：版本位（第 7 字节高 4 位 = 4），变体位（第 9 字节高 2 位 = 10）
        bytes[6] = (bytes[6] & 0x0Fu8) | 0x40u8
        bytes[8] = (bytes[8] & 0x3Fu8) | 0x80u8
        let hex = "0123456789abcdef"
        var sb = StringBuilder()
        for (i in 0..16) {
            if (i == 4 || i == 6 || i == 8 || i == 10) {
                sb.append("-")
            }
            let b = bytes[i]
            // 注意：String 索引返回 UInt8（字节），必须转 Rune 再 append，否则输出十进制 ASCII 码
            sb.append(Rune(UInt32(hex[Int64((b >> 4u8) & 0x0Fu8)])))
            sb.append(Rune(UInt32(hex[Int64(b & 0x0Fu8)])))
        }
        sb.toString()
    }

    /**
     * 将对象序列化为 JSON 字符串。
     * @param obj 任意对象（None 输出 null）。
     */
    public static func json(obj: ?Any): String {
        if (let Some(obj) <- obj) {
            return JsonSerializer.serialize(obj)
        }
        "null"
    }

    /**
     * JSON 字符串转义。
     * @param s 原始字符串。
     * @return 转义后可直接放入 JSON 字符串字面量的内容。
     */
    public static func escapeJson(s: String): String {
        var sb = StringBuilder()
        for (c in s.runes()) {
            match (c) {
                case '"' => sb.append("\\\"")
                case '\\' => sb.append("\\\\")
                case '\n' => sb.append("\\n")
                case '\r' => sb.append("\\r")
                case '\t' => sb.append("\\t")
                case _ => sb.append(c)
            }
        }
        sb.toString()
    }

    /**
     * 从 JSON 字符串反序列化为 T。
     * @param T 目标类型（任意类，无需接口/宏约束）。
     * @param jsonString JSON 字符串。
     * @return 反序列化结果。
     */
    public static func fromJson<T>(jsonString: String): T {
        JsonSerializer.deserialize<T>(jsonString)
    }

    /**
     * 对象 Base64 编码（对象 → JSON → Base64）。
     * @param obj 任意对象（DTO/基础类型/HashMap 等）。
     * @return Base64 字符串。
     */
    public static func base64Encode(obj: Any): String {
        let json = json(Some(obj))
        base64Encode(json)
    }

    /**
     * Base64 → JSON → T 反序列化。
     * @param T 目标类型（任意类，无需接口/宏约束）。
     * @param base64Str Base64 字符串。
     * @return 反序列化结果。
     */
    public static func base64DecodeTo<T>(base64Str: String): T {
        fromJson<T>(base64Decode(base64Str))
    }

    /**
     * 分页。
     */
    public static func paginate<T>(list: Array<T>, page: Int64, count: Int64): Array<T> {
        let p = if (page < 1) { 1 } else { page }
        let c = if (count <= 0) { 10 } else { count }
        let skip = (p - 1) * c
        let total = list.size
        if (skip >= total) {
            return Array<T>()
        }
        let end = if (skip + c > total) { total } else { skip + c }
        list[skip..end]
    }

    // 说明：XmlDeserialize<T> 依赖 System.Xml.Serialization，仓颉生态无 XML 序列化库，未移植。
}
