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

import Foundation
import Observation

@MainActor
/// 钱包实名认证服务协议，抽象实名认证状态读取能力以便测试替换。
protocol WalletRealNameServing {
    /// 获取当前登录用户实名认证状态。
    func realNameInfo() async throws -> RealNameInfoResponse
}

extension ProfileAPI: WalletRealNameServing {}

/// 钱包首页明细 Tab，区分收益明细和提现记录。
enum WalletLedgerTab: String, CaseIterable, Identifiable {
    case earnings
    case withdraws

    var id: String { rawValue }

    /// Tab 展示标题。
    var title: String {
        switch self {
        case .earnings: "收益明细"
        case .withdraws: "提现记录"
        }
    }
}

@MainActor
@Observable
/// 钱包首页 ViewModel，管理钱包汇总、收益明细、提现记录和提现资格流。
final class WalletViewModel {
    var summary: WalletSummaryResponse?
    var pointsOverview: PointOverviewResponse?
    var selectedTab: WalletLedgerTab = .earnings
    var selectedFilter: WalletDateFilter = .last30
    var earningsGroups: [WalletEarningDetailGroup] = []
    var withdrawRecords: [WalletWithdrawRecord] = []
    var isLoadingSummary = false
    var isLoadingList = false
    var isCheckingWithdraw = false
    var errorMessage: String?

    private let pageSize = 20
    private var earningsPage = 1
    private var earningsTotal = 0
    private var withdrawPage = 1
    private var withdrawTotal = 0

    /// 钱包可提现金额展示文本。
    var withdrawableText: String {
        "¥ \(Self.moneyText(summary?.amountWithdrawable))"
    }

    /// 钱包累计金额展示文本。
    var totalAmountText: String {
        "¥ \(Self.moneyText(summary?.amountTotal))"
    }

    /// 当前余额展示文本。
    var currentBalanceText: String {
        "¥ \(Self.moneyText(summary?.amountCurrentBalance))"
    }

    /// 是否还能继续加载收益明细。
    var canLoadMoreEarnings: Bool {
        earningsGroups.flatMap(\.items).count < earningsTotal
    }

    /// 是否还能继续加载提现记录。
    var canLoadMoreWithdraws: Bool {
        withdrawRecords.count < withdrawTotal
    }

    /// 首次加载钱包首页数据。
    func loadInitial(api: WalletServing, staffId: Int?) async {
        isLoadingSummary = true
        errorMessage = nil
        async let summaryResult = api.walletSummary(type: 0)
        async let pointsResult: PointOverviewResponse? = {
            guard let staffId else { return nil }
            return try? await api.pointOverview(staffId: staffId)
        }()

        do {
            summary = try await summaryResult
            pointsOverview = await pointsResult
        } catch {
            errorMessage = error.localizedDescription
        }
        isLoadingSummary = false
        await reloadList(api: api)
    }

    /// 按当前 Tab 和筛选条件刷新列表。
    func reloadList(api: WalletServing) async {
        switch selectedTab {
        case .earnings:
            earningsPage = 1
            await loadEarnings(api: api, reset: true)
        case .withdraws:
            withdrawPage = 1
            await loadWithdraws(api: api, reset: true)
        }
    }

    /// 切换钱包明细 Tab 并刷新对应列表。
    func selectTab(_ tab: WalletLedgerTab, api: WalletServing) async {
        guard selectedTab != tab else { return }
        selectedTab = tab
        await reloadList(api: api)
    }

    /// 切换收益明细时间筛选并重置分页。
    func selectFilter(_ filter: WalletDateFilter, api: WalletServing) async {
        guard selectedFilter != filter else { return }
        selectedFilter = filter
        if selectedTab == .earnings {
            await reloadList(api: api)
        }
    }

    /// 加载下一页当前列表。
    func loadMore(api: WalletServing) async {
        switch selectedTab {
        case .earnings where canLoadMoreEarnings:
            earningsPage += 1
            await loadEarnings(api: api, reset: false)
        case .withdraws where canLoadMoreWithdraws:
            withdrawPage += 1
            await loadWithdraws(api: api, reset: false)
        default:
            break
        }
    }

    /// 根据实名认证和银行卡状态决定提现下一步。
    func resolveWithdrawDecision(profileAPI: WalletRealNameServing, walletAPI: WalletServing) async -> WalletWithdrawDecision {
        isCheckingWithdraw = true
        defer { isCheckingWithdraw = false }

        do {
            let realName = try await profileAPI.realNameInfo().realNameInfo
            guard let realName else {
                return .route(.realNameAuth)
            }
            switch realName.auditStatus {
            case 2:
                let bankCard = try await walletAPI.bankCardInfo().bankCard
                guard let bankCard else {
                    return .route(.bankCardSettings)
                }
                if bankCard.auditStatus == 2 {
                    return .route(.withdrawApply)
                }
                if bankCard.auditStatus == 3 {
                    return .route(.bankCardSettings)
                }
                return .message("银行卡审核中，请审核通过后再试")
            case 3:
                return .route(.realNameAuth)
            default:
                return .message("实名认证审核中，请审核通过后再试")
            }
        } catch {
            return .message(error.localizedDescription)
        }
    }

    /// 加载收益明细分页。
    private func loadEarnings(api: WalletServing, reset: Bool) async {
        isLoadingList = true
        errorMessage = nil
        defer { isLoadingList = false }

        let range = selectedFilter.range()
        do {
            let response = try await api.walletEarningDetail(
                startDate: range.start,
                endDate: range.end,
                page: earningsPage,
                pageSize: pageSize
            )
            earningsTotal = response.total
            earningsGroups = reset ? response.list : earningsGroups + response.list
        } catch {
            if !reset { earningsPage = max(1, earningsPage - 1) }
            if reset { earningsGroups = [] }
            errorMessage = error.localizedDescription
        }
    }

    /// 加载提现记录分页。
    private func loadWithdraws(api: WalletServing, reset: Bool) async {
        isLoadingList = true
        errorMessage = nil
        defer { isLoadingList = false }

        do {
            let response = try await api.walletWithdrawList(page: withdrawPage, pageSize: pageSize)
            withdrawTotal = response.total
            withdrawRecords = reset ? response.item : withdrawRecords + response.item
        } catch {
            if !reset { withdrawPage = max(1, withdrawPage - 1) }
            if reset { withdrawRecords = [] }
            errorMessage = error.localizedDescription
        }
    }

    /// 规范金额展示，空值按 0.00 处理。
    static func moneyText(_ rawValue: String?) -> String {
        let text = rawValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        guard let decimal = Decimal(string: text), decimal > 0 else {
            return "0.00"
        }
        return NSDecimalNumber(decimal: decimal).stringValue
    }
}

@MainActor
@Observable
/// 提现申请 ViewModel，管理提现信息、金额校验、短信验证码和提交。
final class WithdrawApplyViewModel {
    var info: WithdrawInfoResponse?
    var amountText = ""
    var smsCode = ""
    var isLoading = false
    var isSubmitting = false
    var smsCountdown = 0
    var errorMessage: String?

    /// 加载提现申请所需信息。
    func load(api: WalletServing) async {
        isLoading = true
        errorMessage = nil
        defer { isLoading = false }
        do {
            info = try await api.withdrawInfo()
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 发送提现短信验证码。
    func sendSms(api: WalletServing) async {
        guard smsCountdown == 0 else { return }
        do {
            try await api.withdrawSendSms()
            smsCountdown = 60
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 提交提现申请，成功后清空表单。
    func submit(api: WalletServing) async -> Bool {
        guard !isSubmitting else { return false }
        guard let amount = PaymentCollectionViewModel.normalizedMoney(amountText) else {
            errorMessage = "请输入有效提现金额"
            return false
        }
        guard let info else {
            errorMessage = "提现信息未加载"
            return false
        }
        guard (Decimal(string: amount) ?? 0) <= (Decimal(string: info.amountWithdrawable) ?? 0) else {
            errorMessage = "提现金额不能超过可提现金额"
            return false
        }
        guard !smsCode.walletTrimmed.isEmpty else {
            errorMessage = "请输入短信验证码"
            return false
        }

        isSubmitting = true
        defer { isSubmitting = false }
        do {
            try await api.withdrawApply(amount: amount, smsCode: smsCode.walletTrimmed)
            amountText = ""
            smsCode = ""
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }
}

@MainActor
@Observable
/// 银行卡设置 ViewModel，管理银行卡表单、图片上传、短信验证码和提交。
final class WithdrawalSettingsViewModel {
    var bankCard: WalletBankCardInfo?
    var banks: [String] = []
    var areas: [AreaNode] = []
    var realName = ""
    var cardNumber = ""
    var bankName = ""
    var branchName = ""
    var provinceCode = ""
    var cityCode = ""
    var smsCode = ""
    var frontImageData: Data?
    var backImageData: Data?
    var frontImageURL = ""
    var backImageURL = ""
    var uploadProgress = 0
    var isLoading = false
    var isSubmitting = false
    var smsCountdown = 0
    var errorMessage: String?

    /// 加载银行卡设置需要的银行、地区和已提交资料。
    func load(api: WalletServing) async {
        isLoading = true
        errorMessage = nil
        defer { isLoading = false }
        async let bankCardResult = api.bankCardInfo()
        async let bankListResult = api.bankList()
        async let areaResult = api.areas()
        do {
            let (cardResponse, bankResponse, areaResponse) = try await (bankCardResult, bankListResult, areaResult)
            bankCard = cardResponse.bankCard
            banks = bankResponse.banks
            areas = areaResponse
            apply(cardResponse.bankCard)
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 发送银行卡设置短信验证码。
    func sendSms(api: WalletServing) async {
        guard smsCountdown == 0 else { return }
        do {
            try await api.bankCardVerifyCode()
            smsCountdown = 60
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 上传图片并提交银行卡资料。
    func submit(api: WalletServing, uploader: OSSUploadServing, scenicId: Int?) async -> Bool {
        guard !isSubmitting else { return false }
        guard let scenicId else {
            errorMessage = "请先选择景区"
            return false
        }
        guard validateForm() else { return false }

        isSubmitting = true
        uploadProgress = 0
        defer { isSubmitting = false }

        do {
            let frontURL = try await resolvedImageURL(
                currentURL: frontImageURL,
                data: frontImageData,
                fileName: "bank_card_front.jpg",
                uploader: uploader,
                scenicId: scenicId
            )
            let backURL = try await resolvedImageURL(
                currentURL: backImageURL,
                data: backImageData,
                fileName: "bank_card_back.jpg",
                uploader: uploader,
                scenicId: scenicId
            )
            let request = UpdateBankInfoRequest(
                realName: realName.walletTrimmed,
                cardNumber: cardNumber.walletTrimmed,
                bankName: bankName.walletTrimmed,
                branchName: branchName.walletTrimmed,
                frontUrl: frontURL,
                backUrl: backURL,
                provinceCode: provinceCode.walletTrimmed,
                cityCode: cityCode.walletTrimmed,
                smsVerifyCode: smsCode.walletTrimmed
            )
            try await api.updateBankInfo(request)
            frontImageURL = frontURL
            backImageURL = backURL
            frontImageData = nil
            backImageData = nil
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }

    /// 将已有银行卡资料回填到表单。
    private func apply(_ info: WalletBankCardInfo?) {
        guard let info else { return }
        realName = info.realName
        cardNumber = info.cardNumber
        bankName = info.bankName
        branchName = info.branchName
        provinceCode = info.provinceCode ?? ""
        cityCode = info.cityCode ?? ""
        frontImageURL = info.frontUrl ?? ""
        backImageURL = info.backUrl ?? ""
    }

    /// 校验银行卡表单必填项。
    private func validateForm() -> Bool {
        if realName.walletTrimmed.isEmpty { errorMessage = "请输入持卡人姓名"; return false }
        if cardNumber.walletTrimmed.isEmpty { errorMessage = "请输入银行卡号"; return false }
        if bankName.walletTrimmed.isEmpty { errorMessage = "请选择开户银行"; return false }
        if branchName.walletTrimmed.isEmpty { errorMessage = "请输入开户支行"; return false }
        if provinceCode.walletTrimmed.isEmpty || cityCode.walletTrimmed.isEmpty { errorMessage = "请选择开户地区"; return false }
        if frontImageURL.walletTrimmed.isEmpty && frontImageData == nil { errorMessage = "请选择银行卡正面照片"; return false }
        if backImageURL.walletTrimmed.isEmpty && backImageData == nil { errorMessage = "请选择银行卡反面照片"; return false }
        if smsCode.walletTrimmed.isEmpty { errorMessage = "请输入短信验证码"; return false }
        return true
    }

    /// 返回最终图片 URL，本地选择的新图会先上传 OSS。
    private func resolvedImageURL(
        currentURL: String,
        data: Data?,
        fileName: String,
        uploader: OSSUploadServing,
        scenicId: Int
    ) async throws -> String {
        guard let data else { return currentURL }
        return try await uploader.uploadBankCardImage(data: data, fileName: fileName, scenicId: scenicId) { [weak self] progress in
            self?.uploadProgress = progress
        }
    }
}

@MainActor
@Observable
/// 积分兑换 ViewModel，管理积分概览、兑换记录、兑换金额校验和提交。
final class PointsRedemptionViewModel {
    var overview = PointOverviewResponse()
    var records: [PointWithdrawItem] = []
    var pointsText = ""
    var isLoading = false
    var isSubmitting = false
    var errorMessage: String?

    private let pageSize = 20
    private var page = 1
    private var total = 0

    /// 是否还能继续加载兑换记录。
    var canLoadMore: Bool {
        records.count < total
    }

    /// 加载积分概览和兑换记录。
    func load(api: WalletServing, staffId: Int?) async {
        guard let staffId else {
            errorMessage = "缺少账号信息"
            return
        }
        isLoading = true
        errorMessage = nil
        page = 1
        defer { isLoading = false }
        do {
            async let overviewResult = api.pointOverview(staffId: staffId)
            async let listResult = api.pointWithdrawList(status: nil, page: page, pageSize: pageSize)
            overview = try await overviewResult
            let list = try await listResult
            total = list.total
            records = list.list
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 加载下一页积分兑换记录。
    func loadMore(api: WalletServing) async {
        guard canLoadMore else { return }
        page += 1
        do {
            let response = try await api.pointWithdrawList(status: nil, page: page, pageSize: pageSize)
            total = response.total
            records += response.list
        } catch {
            page = max(1, page - 1)
            errorMessage = error.localizedDescription
        }
    }

    /// 将可兑换积分全部填入表单。
    func fillAllPoints() {
        pointsText = String(overview.withdrawnPoints)
    }

    /// 提交积分兑换申请，成功后刷新概览和列表。
    func submit(api: WalletServing, staffId: Int?) async -> Bool {
        guard !isSubmitting else { return false }
        guard let points = Int(pointsText.walletTrimmed), points > 0 else {
            errorMessage = "请输入有效兑换积分"
            return false
        }
        guard points <= overview.withdrawnPoints else {
            errorMessage = "兑换积分不能超过可兑换积分"
            return false
        }
        guard let staffId else {
            errorMessage = "缺少账号信息"
            return false
        }

        isSubmitting = true
        defer { isSubmitting = false }
        do {
            try await api.pointWithdrawApply(points: points, remark: "积分提现申请")
            pointsText = ""
            await load(api: api, staffId: staffId)
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }
}

private extension String {
    /// 钱包模块内部使用的去空白文本，避免依赖其他文件的 fileprivate 扩展。
    var walletTrimmed: String {
        trimmingCharacters(in: .whitespacesAndNewlines)
    }
}
