//
//  RealNameAuthViewModel.swift
//  suixinkan
//

import Foundation

/// 实名认证页面模式，描述首次申请与各审核状态下的编辑权限。
enum RealNameAuthMode: Equatable {
    case newApplication
    case pending
    case approved
    case rejected

    /// 页面是否只允许查看资料。
    var isReadOnly: Bool {
        self == .pending || self == .approved
    }

    /// 页面是否允许编辑并提交资料。
    var isEditable: Bool {
        !isReadOnly
    }
}

/// 实名认证两步表单的当前步骤。
enum RealNameAuthStep: Int, Equatable {
    case identity = 1
    case verification = 2
}

/// 实名认证 ViewModel，管理两步表单、审核模式、图片上传、验证码和提交状态。
final class RealNameAuthViewModel {
    private(set) var info: RealNameInfo?
    private(set) var mode: RealNameAuthMode
    private(set) var currentStep: RealNameAuthStep = .identity
    private(set) var realName = ""
    private(set) var idCardNo = ""
    private(set) var smsCode = ""
    private(set) var startDate: Date?
    private(set) var endDate: Date?
    private(set) var isLongValid = false
    private(set) var isAgreed = false
    private(set) var frontUrl = ""
    private(set) var backUrl = ""
    private(set) var frontPreviewImageData: Data?
    private(set) var backPreviewImageData: Data?
    private(set) var frontUploadProgress = 0
    private(set) var backUploadProgress = 0
    private(set) var isFrontUploading = false
    private(set) var isBackUploading = false
    private(set) var loading = false
    private(set) var sendingCode = false
    private(set) var submitting = false
    private(set) var otpCountdown = 0
    private(set) var statusMessage: String?

    private var countdownTask: Task<Void, Never>?

    /// 页面状态变化后通知 View 主动刷新。
    var onStateChange: (() -> Void)?

    /// 使用可选审核信息创建表单；无信息表示首次申请。
    init(info: RealNameInfo? = nil) {
        self.info = info
        mode = Self.mode(for: info)
        apply(info)
    }

    deinit {
        countdownTask?.cancel()
    }

    /// 当前是否存在任一证件图片上传任务。
    var isUploading: Bool {
        isFrontUploading || isBackUploading
    }

    /// 第一步是否满足进入短信验证步骤的条件。
    var canGoNextStep: Bool {
        guard mode.isEditable, currentStep == .identity, !isUploading else { return false }
        return identityValidationMessage == nil && isAgreed
    }

    /// 第二步是否满足最终提交条件。
    var canSubmit: Bool {
        guard mode.isEditable, currentStep == .verification, !submitting, !isUploading else { return false }
        return validationMessage == nil
    }

    /// 验证码按钮是否可点击。
    var canRequestCode: Bool {
        mode.isEditable && currentStep == .verification && otpCountdown == 0 && !sendingCode
    }

    /// 更新真实姓名。
    func updateRealName(_ value: String) {
        guard mode.isEditable else { return }
        realName = value
        notifyStateChange()
    }

    /// 更新身份证号，只保留数字和 X 并统一为大写。
    func updateIdCardNo(_ value: String) {
        guard mode.isEditable else { return }
        idCardNo = String(value.filter { $0.isNumber || $0 == "X" || $0 == "x" }.prefix(18)).uppercased()
        notifyStateChange()
    }

    /// 更新短信验证码，只保留最多 11 位数字。
    func updateSmsCode(_ value: String) {
        guard mode.isEditable else { return }
        smsCode = String(value.filter(\.isNumber).prefix(11))
        notifyStateChange()
    }

    /// 切换协议同意状态。
    func toggleAgreement() {
        guard mode.isEditable else { return }
        isAgreed.toggle()
        notifyStateChange()
    }

    /// 更新长期有效状态；开启后清空截止日期。
    func updateLongValid(_ value: Bool) {
        guard mode.isEditable else { return }
        isLongValid = value
        if value {
            endDate = nil
        }
        notifyStateChange()
    }

    /// 更新证件有效期起始日期。
    func updateStartDate(_ value: Date) {
        guard mode.isEditable else { return }
        startDate = Calendar.current.startOfDay(for: value)
        notifyStateChange()
    }

    /// 更新证件有效期截止日期。
    func updateEndDate(_ value: Date) {
        guard mode.isEditable, !isLongValid else { return }
        endDate = Calendar.current.startOfDay(for: value)
        notifyStateChange()
    }

    /// 从服务端重新拉取实名认证信息并应用到页面。
    func load(api: ProfileAPI) async throws {
        guard !loading else { return }
        loading = true
        notifyStateChange()
        defer {
            loading = false
            notifyStateChange()
        }
        let response = try await api.realNameInfo()
        info = response.realNameInfo
        mode = Self.mode(for: response.realNameInfo)
        apply(response.realNameInfo)
    }

    /// 进入短信验证步骤；校验失败时返回对应提示。
    @discardableResult
    func goToVerificationStep() -> String? {
        guard mode.isEditable else {
            currentStep = .verification
            notifyStateChange()
            return nil
        }
        if let identityValidationMessage {
            return identityValidationMessage
        }
        guard isAgreed else { return "请阅读并同意用户协议与隐私政策" }
        currentStep = .verification
        notifyStateChange()
        return nil
    }

    /// 返回证件资料步骤。
    func backToIdentityStep() {
        currentStep = .identity
        notifyStateChange()
    }

    /// 发送实名认证短信验证码并启动 60 秒倒计时。
    func sendCode(api: ProfileAPI) async throws {
        guard canRequestCode else { return }
        sendingCode = true
        notifyStateChange()
        defer {
            sendingCode = false
            notifyStateChange()
        }
        try await api.realNameSmsVerifyCode()
        statusMessage = "验证码发送成功"
        startCountdown()
        notifyStateChange()
    }

    /// 处理并立即上传身份证图片。
    func uploadIdentityImage(
        data: Data,
        side: RealNameImageSide,
        uploader: any OSSUploadServing,
        scenicId: Int,
        timestamp: TimeInterval = Date().timeIntervalSince1970
    ) async throws {
        guard mode.isEditable else { return }
        guard !uploading(side) else { return }

        let processed = try RealNameImageProcessor.process(data: data, side: side, timestamp: timestamp)
        setPreview(processed.data, side: side)
        setUploading(true, progress: 0, side: side)
        defer { setUploading(false, progress: progress(for: side), side: side) }

        do {
            let url = try await uploader.uploadRealNameImage(
                data: processed.data,
                fileName: processed.fileName,
                scenicId: scenicId
            ) { [weak self] progress in
                self?.setProgress(progress, side: side)
            }
            setURL(url, side: side)
            setProgress(100, side: side)
        } catch {
            setPreview(nil, side: side)
            setURL("", side: side)
            throw error
        }
    }

    /// 删除指定一面的证件图片。
    func deleteIdentityImage(side: RealNameImageSide) {
        guard mode.isEditable, !uploading(side) else { return }
        setPreview(nil, side: side)
        setURL("", side: side)
        setProgress(0, side: side)
        notifyStateChange()
    }

    /// 提交实名认证资料。
    func submit(api: ProfileAPI) async throws {
        guard mode.isEditable, !submitting else { return }
        if let validationMessage {
            throw RealNameValidationError.message(validationMessage)
        }

        submitting = true
        notifyStateChange()
        defer {
            submitting = false
            notifyStateChange()
        }
        try await api.realNameSubmit(try makeRequest())
        statusMessage = "认证成功"
        notifyStateChange()
    }

    /// 当前完整表单的首个校验错误。
    var validationMessage: String? {
        if let identityValidationMessage { return identityValidationMessage }
        if !isAgreed { return "请阅读并同意用户协议与隐私政策" }
        if smsCode.trimmed.isEmpty { return "请输入短信验证码" }
        return nil
    }

    /// 生成与 Android DTO 对齐的实名认证提交请求。
    func makeRequest() throws -> RealNameAuthRequest {
        if let validationMessage {
            throw RealNameValidationError.message(validationMessage)
        }
        guard let startDate else {
            throw RealNameValidationError.message("请选择证件起始日期")
        }
        return RealNameAuthRequest(
            realName: realName.trimmed,
            idCardNo: Self.normalizedIDCardNumber(idCardNo),
            smsVerifyCode: smsCode.trimmed,
            startDate: Self.dateFormatter.string(from: startDate),
            endDate: isLongValid ? "" : endDate.map(Self.dateFormatter.string(from:)) ?? "",
            isLongValid: isLongValid ? 1 : 0,
            frontUrl: frontUrl.trimmed,
            backUrl: backUrl.trimmed
        )
    }

    /// 规范化身份证号码，移除首尾空白并统一大写。
    nonisolated static func normalizedIDCardNumber(_ value: String) -> String {
        value.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
    }

    /// 校验大陆 18 位身份证号码的格式和校验码。
    nonisolated static func isValidMainlandIDCardNumber(_ value: String) -> Bool {
        let number = normalizedIDCardNumber(value)
        guard number.count == 18 else { return false }
        let chars = Array(number)
        guard chars.prefix(17).allSatisfy(\.isNumber) else { return false }
        guard chars[17].isNumber || chars[17] == "X" else { return false }
        let weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
        let checkCodes = Array("10X98765432")
        let sum = zip(chars.prefix(17), weights).reduce(0) { partial, pair in
            partial + (pair.0.wholeNumberValue ?? 0) * pair.1
        }
        return chars[17] == checkCodes[sum % 11]
    }

    private var identityValidationMessage: String? {
        if realName.trimmed.isEmpty { return "请输入真实姓名" }
        if idCardNo.trimmed.isEmpty { return "请输入身份证号" }
        if !Self.isValidMainlandIDCardNumber(idCardNo) { return "请输入有效的身份证号" }
        if backUrl.trimmed.isEmpty { return "请选择身份证国徽面图片" }
        if frontUrl.trimmed.isEmpty { return "请选择身份证人像面图片" }
        guard let startDate else { return "请选择证件起始日期" }
        if !isLongValid {
            guard let endDate else { return "请选择证件截止日期" }
            if endDate < startDate { return "证件结束日期不能早于起始日期" }
        }
        return nil
    }

    private func apply(_ info: RealNameInfo?) {
        guard let info else {
            isAgreed = false
            return
        }
        realName = info.realName
        idCardNo = info.idCardNo
        frontUrl = info.frontUrl ?? ""
        backUrl = info.backUrl ?? ""
        frontPreviewImageData = nil
        backPreviewImageData = nil
        isLongValid = info.isLongValid
        startDate = Self.date(from: info.startDate)
        endDate = info.isLongValid ? nil : Self.date(from: info.endDate)
        isAgreed = true
    }

    private static func mode(for info: RealNameInfo?) -> RealNameAuthMode {
        guard let info else { return .newApplication }
        switch info.auditStatus {
        case 2: return .approved
        case 3: return .rejected
        default: return .pending
        }
    }

    private func uploading(_ side: RealNameImageSide) -> Bool {
        side == .front ? isFrontUploading : isBackUploading
    }

    private func progress(for side: RealNameImageSide) -> Int {
        side == .front ? frontUploadProgress : backUploadProgress
    }

    private func setPreview(_ data: Data?, side: RealNameImageSide) {
        if side == .front {
            frontPreviewImageData = data
        } else {
            backPreviewImageData = data
        }
        notifyStateChange()
    }

    private func setURL(_ url: String, side: RealNameImageSide) {
        if side == .front {
            frontUrl = url
        } else {
            backUrl = url
        }
        notifyStateChange()
    }

    private func setUploading(_ uploading: Bool, progress: Int, side: RealNameImageSide) {
        if side == .front {
            isFrontUploading = uploading
            frontUploadProgress = progress
        } else {
            isBackUploading = uploading
            backUploadProgress = progress
        }
        notifyStateChange()
    }

    private func setProgress(_ progress: Int, side: RealNameImageSide) {
        let normalized = max(0, min(100, progress))
        if side == .front {
            frontUploadProgress = normalized
        } else {
            backUploadProgress = normalized
        }
        notifyStateChange()
    }

    private func startCountdown(seconds: Int = 60) {
        countdownTask?.cancel()
        otpCountdown = seconds
        countdownTask = Task { [weak self] in
            guard seconds > 0 else { return }
            for remaining in stride(from: seconds - 1, through: 0, by: -1) {
                do {
                    try await Task.sleep(for: .seconds(1))
                } catch {
                    return
                }
                guard let self else { return }
                self.otpCountdown = remaining
                self.notifyStateChange()
            }
        }
    }

    nonisolated private static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .gregorian)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter
    }()

    private static func date(from value: String?) -> Date? {
        guard let value, !value.isEmpty else { return nil }
        return dateFormatter.date(from: value)
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}

/// 实名认证表单校验错误。
enum RealNameValidationError: LocalizedError, Equatable {
    case message(String)

    /// 面向用户展示的校验提示。
    var errorDescription: String? {
        switch self {
        case .message(let message): message
        }
    }
}

private extension String {
    var trimmed: String {
        trimmingCharacters(in: .whitespacesAndNewlines)
    }
}
