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

import Foundation

/// 实名认证 ViewModel，管理认证资料表单、审核状态、短信验证码和提交状态。
final class RealNameAuthViewModel {
    private(set) var info: RealNameInfo?
    private(set) var realName = ""
    private(set) var idCardNo = ""
    private(set) var smsCode = ""
    private(set) var startDate = Date()
    private(set) var endDate = Calendar.current.date(byAdding: .year, value: 10, to: Date()) ?? Date()
    private(set) var isLongValid = false
    private(set) var frontUrl = ""
    private(set) var backUrl = ""
    private(set) var pendingFrontImageData: Data?
    private(set) var pendingBackImageData: Data?
    private(set) var loading = false
    private(set) var sendingCode = false
    private(set) var submitting = false
    private(set) var statusMessage: String?

    private var pendingFrontFileName: String?
    private var pendingBackFileName: String?

    var onStateChange: (() -> Void)?

    func updateRealName(_ value: String) {
        realName = value
        notifyStateChange()
    }

    func updateIdCardNo(_ value: String) {
        idCardNo = value
        notifyStateChange()
    }

    func updateSmsCode(_ value: String) {
        smsCode = value
        notifyStateChange()
    }

    func updateLongValid(_ value: Bool) {
        isLongValid = value
        notifyStateChange()
    }

    func updateStartDate(_ value: Date) {
        startDate = value
        notifyStateChange()
    }

    func updateEndDate(_ value: Date) {
        endDate = 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()
        apply(response.realNameInfo)
    }

    func sendCode(api: ProfileAPI) async throws {
        guard !sendingCode else { return }
        sendingCode = true
        notifyStateChange()
        defer {
            sendingCode = false
            notifyStateChange()
        }
        try await api.realNameSmsVerifyCode()
        statusMessage = "验证码已发送"
        notifyStateChange()
    }

    func prepareIdentityImage(data: Data, side: RealNameImageSide, timestamp: TimeInterval = Date().timeIntervalSince1970) throws {
        let processed = try RealNameImageProcessor.process(data: data, side: side, timestamp: timestamp)
        switch side {
        case .front:
            pendingFrontImageData = processed.data
            pendingFrontFileName = processed.fileName
            frontUrl = ""
        case .back:
            pendingBackImageData = processed.data
            pendingBackFileName = processed.fileName
            backUrl = ""
        }
        notifyStateChange()
    }

    func submit(api: ProfileAPI, uploader: any OSSUploadServing, scenicId: Int) async throws {
        if info?.verified == true {
            statusMessage = "已完成实名认证"
            notifyStateChange()
            return
        }
        if let validationMessage {
            throw RealNameValidationError.message(validationMessage)
        }

        submitting = true
        notifyStateChange()
        defer {
            submitting = false
            notifyStateChange()
        }
        try await uploadPendingIdentityImages(uploader: uploader, scenicId: scenicId)
        try await api.realNameSubmit(makeRequest())
        statusMessage = "已提交审核"
        notifyStateChange()
        try await load(api: api)
    }

    var validationMessage: String? {
        if realName.trimmed.isEmpty { return "请输入真实姓名" }
        if idCardNo.trimmed.isEmpty { return "请输入身份证号" }
        if !Self.isValidMainlandIDCardNumber(idCardNo) { return "请输入有效的身份证号" }
        if info?.verified != true {
            if smsCode.trimmed.isEmpty { return "请输入短信验证码" }
            if frontUrl.trimmed.isEmpty && pendingFrontImageData == nil { return "请选择身份证人像面图片" }
            if backUrl.trimmed.isEmpty && pendingBackImageData == nil { return "请选择身份证国徽面图片" }
        }
        if !isLongValid && endDate < startDate { return "证件结束日期不能早于起始日期" }
        return nil
    }

    nonisolated static func normalizedIDCardNumber(_ value: String) -> String {
        value.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
    }

    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 func apply(_ info: RealNameInfo?) {
        self.info = info
        guard let info else {
            notifyStateChange()
            return
        }
        realName = info.realName
        idCardNo = info.idCardNo
        frontUrl = info.frontUrl ?? ""
        backUrl = info.backUrl ?? ""
        pendingFrontImageData = nil
        pendingBackImageData = nil
        isLongValid = info.isLongValid
        startDate = Self.date(from: info.startDate) ?? startDate
        endDate = Self.date(from: info.endDate) ?? endDate
        notifyStateChange()
    }

    private func makeRequest() -> RealNameAuthRequest {
        RealNameAuthRequest(
            realName: realName.trimmed,
            idCardNo: Self.normalizedIDCardNumber(idCardNo),
            smsVerifyCode: smsCode.trimmed,
            startDate: Self.dateFormatter.string(from: startDate),
            endDate: isLongValid ? "" : Self.dateFormatter.string(from: endDate),
            isLongValid: isLongValid ? 1 : 0,
            frontUrl: frontUrl.trimmed,
            backUrl: backUrl.trimmed
        )
    }

    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 uploadPendingIdentityImages(uploader: any OSSUploadServing, scenicId: Int) async throws {
        if let pendingFrontImageData {
            frontUrl = try await uploader.uploadRealNameImage(
                data: pendingFrontImageData,
                fileName: pendingFrontFileName ?? "real_name_front.jpg",
                scenicId: scenicId
            ) { _ in }
            self.pendingFrontImageData = nil
        }
        if let pendingBackImageData {
            backUrl = try await uploader.uploadRealNameImage(
                data: pendingBackImageData,
                fileName: pendingBackFileName ?? "real_name_back.jpg",
                scenicId: scenicId
            ) { _ in }
            self.pendingBackImageData = nil
        }
    }

    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)
    }
}
