//
//  RealNameAuthViewModel.swift
//  suixinkan
//
//  Created by Codex on 2026/6/22.
//

import Foundation
import Observation

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

    /// 拉取实名认证信息，并回填表单。
    func load(api: ProfileAPI) async throws {
        guard !loading else { return }
        loading = true
        defer { loading = false }

        let response = try await api.realNameInfo()
        apply(response.realNameInfo)
    }

    /// 发送短信验证码。
    func sendCode(api: ProfileAPI) async throws {
        guard !sendingCode else { return }
        sendingCode = true
        defer { sendingCode = false }

        try await api.realNameSmsVerifyCode()
        statusMessage = "验证码已发送"
    }

    /// 准备待上传证件图片，并更新对应面的本地预览。
    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 = ""
        }
    }

    /// 提交实名认证资料，成功后刷新审核状态。
    func submit(api: ProfileAPI, uploader: any OSSUploadServing, scenicId: Int) async throws {
        if info?.verified == true {
            statusMessage = "已完成实名认证"
            return
        }
        if let validationMessage {
            throw RealNameValidationError.message(validationMessage)
        }

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

    /// 根据审核状态生成展示文案。
    func auditStatusText(_ status: Int) -> String {
        switch status {
        case 1: "待审核"
        case 2: "审核通过"
        case 3: "审核不通过"
        default: "未认证"
        }
    }

    /// 当前表单校验错误，nil 表示可以提交。
    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
    }

    /// 应用接口返回的实名认证信息。
    private func apply(_ info: RealNameInfo?) {
        self.info = info
        guard let info else { return }
        realName = info.realName
        idCardNo = info.idCardNo
        frontUrl = info.frontUrl ?? ""
        backUrl = info.backUrl ?? ""
        pendingFrontImageData = nil
        pendingBackImageData = nil
        pendingFrontFileName = nil
        pendingBackFileName = nil
        isLongValid = info.isLongValid
        startDate = Self.date(from: info.startDate) ?? startDate
        endDate = Self.date(from: info.endDate) ?? endDate
    }

    /// 生成提交请求。
    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
        )
    }

    /// 规范化身份证号，去除空白并大写末位 X。
    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 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
    }()

    /// 将 yyyy-MM-dd 文本解析为日期。
    private static func date(from value: String?) -> Date? {
        guard let value, !value.isEmpty else { return nil }
        return dateFormatter.date(from: value)
    }

    /// 上传待提交证件图片，并把返回 URL 写回表单字段。
    private func uploadPendingIdentityImages(uploader: any OSSUploadServing, scenicId: Int) async throws {
        if let pendingFrontImageData {
            frontUploadProgress = 1
            defer { frontUploadProgress = nil }
            frontUrl = try await uploader.uploadRealNameImage(
                data: pendingFrontImageData,
                fileName: pendingFrontFileName ?? "real_name_front_\(Int(Date().timeIntervalSince1970)).jpg",
                scenicId: scenicId
            ) { [weak self] progress in
                Task { @MainActor in
                    self?.frontUploadProgress = progress
                }
            }
            self.pendingFrontImageData = nil
            pendingFrontFileName = nil
        }

        if let pendingBackImageData {
            backUploadProgress = 1
            defer { backUploadProgress = nil }
            backUrl = try await uploader.uploadRealNameImage(
                data: pendingBackImageData,
                fileName: pendingBackFileName ?? "real_name_back_\(Int(Date().timeIntervalSince1970)).jpg",
                scenicId: scenicId
            ) { [weak self] progress in
                Task { @MainActor in
                    self?.backUploadProgress = progress
                }
            }
            self.pendingBackImageData = nil
            pendingBackFileName = nil
        }
    }
}

/// 实名认证校验错误实体，用于把表单错误统一抛给页面展示。
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)
    }
}
