//
//  WalletViewModels.swift
//  suixinkan
//

import Foundation

@MainActor
/// 钱包前置资料服务协议，提供提现前实名和银行卡状态检查。
protocol WalletProfileServing {
    /// 拉取当前登录用户的实名认证状态。
    func realNameInfo() async throws -> RealNameInfoResponse

    /// 拉取当前登录用户的银行卡信息。
    func bankCardInfo() async throws -> BankCardInfoResponse
}

extension ProfileAPI: WalletProfileServing {}

/// 钱包首页 Tab。
enum WalletTab: Int, CaseIterable {
    case withdraw
    case transaction

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

/// 钱包收益明细日期筛选。
enum WalletFilter: Int, CaseIterable {
    case last7Days
    case last30Days
    case last180Days

    /// 展示标题。
    var title: String {
        switch self {
        case .last7Days: "最近7天"
        case .last30Days: "最近30天"
        case .last180Days: "最近180天"
        }
    }

    /// 向前取数天数。
    var dayOffset: Int {
        switch self {
        case .last7Days: 7
        case .last30Days: 30
        case .last180Days: 180
        }
    }
}

/// 钱包提现按钮检查后的导航目标。
enum WalletWithdrawDestination: Equatable {
    case realNameAuth
    case realNameAudit(RealNameInfo)
    case realNamePending
    case withdrawalSettings
    case withdrawalAudit(BankCardInfo)
    case bankCardPending
    case withdraw
}

/// 钱包首页 ViewModel，管理汇总、提现记录、收益明细、积分概览和提现前置分流。
final class WalletViewModel {
    private(set) var summary: WalletSummaryResponse?
    private(set) var selectedTab: WalletTab = .withdraw
    private(set) var filter: WalletFilter = .last7Days
    private(set) var withdrawRecords: [WalletWithdrawItem] = []
    private(set) var transactionGroups: [EarningDetailGroup] = []
    private(set) var totalIncomeLabel = "¥ 0.00"
    private(set) var totalPointsLabel = 0
    private(set) var availablePoints = 0
    private(set) var summaryLoading = false
    private(set) var withdrawLoading = false
    private(set) var withdrawRefreshing = false
    private(set) var transactionLoading = false
    private(set) var transactionRefreshing = false
    private(set) var pointsLoading = false
    private(set) var errorMessage: String?

    var onStateChange: (() -> Void)?

    private let staffIdProvider: () -> Int?
    private let calendar: Calendar
    private let nowProvider: () -> Date
    private var withdrawPage = 1
    private var withdrawCanLoadMore = true
    private var transactionPage = 1
    private var transactionCanLoadMore = true

    private let pageSize = 10

    /// 初始化钱包首页 ViewModel。
    init(
        staffIdProvider: @escaping () -> Int? = {
            Int(AppStore.shared.session.userId.trimmingCharacters(in: .whitespacesAndNewlines))
        },
        calendar: Calendar = Calendar(identifier: .gregorian),
        nowProvider: @escaping () -> Date = Date.init
    ) {
        self.staffIdProvider = staffIdProvider
        self.calendar = calendar
        self.nowProvider = nowProvider
    }

    /// 首次加载钱包首页全部首屏数据。
    func loadInitial(api: any WalletPageServing) async {
        await refreshSummary(api: api)
        await refreshWithdraw(api: api)
        await refreshTransaction(api: api)
        await refreshPoints(api: api)
    }

    /// 刷新钱包金额汇总。
    func refreshSummary(api: any WalletPageServing) async {
        summaryLoading = true
        errorMessage = nil
        notifyStateChange()
        do {
            summary = try await api.walletSummary(type: 0)
        } catch {
            errorMessage = error.localizedDescription
        }
        summaryLoading = false
        notifyStateChange()
    }

    /// 刷新当前可用积分。
    func refreshPoints(api: any WalletPageServing) async {
        guard let staffId = staffIdProvider(), staffId > 0 else { return }
        pointsLoading = true
        notifyStateChange()
        do {
            let overview = try await api.pointOverview(staffId: staffId)
            availablePoints = overview.availablePoints
        } catch {
            errorMessage = error.localizedDescription
        }
        pointsLoading = false
        notifyStateChange()
    }

    /// 切换钱包 Tab。
    func selectTab(_ tab: WalletTab, api: any WalletPageServing) async {
        guard tab != selectedTab else { return }
        selectedTab = tab
        notifyStateChange()
        switch tab {
        case .withdraw:
            if withdrawRecords.isEmpty { await refreshWithdraw(api: api) }
        case .transaction:
            if transactionGroups.isEmpty { await refreshTransaction(api: api) }
        }
    }

    /// 切换收益明细筛选。
    func selectFilter(_ nextFilter: WalletFilter, api: any WalletPageServing) async {
        guard nextFilter != filter else { return }
        filter = nextFilter
        notifyStateChange()
        await refreshTransaction(api: api)
    }

    /// 刷新提现记录。
    func refreshWithdraw(api: any WalletPageServing) async {
        await loadWithdraw(api: api, refresh: true)
    }

    /// 仅刷新当前选中的钱包 Tab。
    func refreshSelectedTab(api: any WalletPageServing) async {
        switch selectedTab {
        case .withdraw:
            await refreshWithdraw(api: api)
        case .transaction:
            await refreshTransaction(api: api)
        }
    }

    /// 滚动到底部时加载更多提现记录。
    func loadMoreWithdrawIfNeeded(currentIndex: Int, api: any WalletPageServing) async {
        guard currentIndex >= withdrawRecords.count - 2, withdrawCanLoadMore, !withdrawLoading else { return }
        await loadWithdraw(api: api, refresh: false)
    }

    /// 刷新收益明细。
    func refreshTransaction(api: any WalletPageServing) async {
        await loadTransaction(api: api, refresh: true)
    }

    /// 滚动到底部时加载更多收益明细。
    func loadMoreTransactionIfNeeded(currentIndex: Int, api: any WalletPageServing) async {
        let count = transactionEntries.count
        guard currentIndex >= count - 2, transactionCanLoadMore, !transactionLoading else { return }
        await loadTransaction(api: api, refresh: false)
    }

    /// 收益明细列表条目，将分组头和明细扁平化。
    var transactionEntries: [WalletTransactionEntry] {
        transactionGroups.flatMap { group -> [WalletTransactionEntry] in
            [.header(group)] + group.items.map(WalletTransactionEntry.item)
        }
    }

    /// 检查提现前置状态并返回页面导航目标。
    func resolveWithdrawDestination(profileAPI: any WalletProfileServing) async throws -> WalletWithdrawDestination {
        let realName = try await profileAPI.realNameInfo().realNameInfo
        guard let realName else { return .realNameAuth }
        switch realName.auditStatus {
        case 2:
            break
        case 3:
            return .realNameAudit(realName)
        default:
            return .realNamePending
        }

        let bankCard = try await profileAPI.bankCardInfo().bankCard
        guard let bankCard else { return .withdrawalSettings }
        switch bankCard.auditStatus {
        case 2:
            return .withdraw
        case 3:
            return .withdrawalAudit(bankCard)
        default:
            return .bankCardPending
        }
    }

    private func loadWithdraw(api: any WalletPageServing, refresh: Bool) async {
        if withdrawLoading && !refresh { return }
        let targetPage = refresh ? 1 : withdrawPage
        let previousPage = withdrawPage
        let previousCanLoadMore = withdrawCanLoadMore
        withdrawLoading = true
        withdrawRefreshing = refresh
        errorMessage = nil
        if refresh {
            withdrawCanLoadMore = true
            withdrawPage = 1
        }
        notifyStateChange()

        do {
            let response = try await api.walletWithdrawList(page: targetPage, pageSize: pageSize)
            withdrawRecords = refresh ? response.item : withdrawRecords + response.item
            withdrawCanLoadMore = withdrawRecords.count < response.total
            withdrawPage = response.item.isEmpty ? targetPage : targetPage + 1
        } catch {
            if refresh {
                withdrawPage = previousPage
                withdrawCanLoadMore = previousCanLoadMore
            }
            errorMessage = error.localizedDescription
        }

        withdrawLoading = false
        withdrawRefreshing = false
        notifyStateChange()
    }

    private func loadTransaction(api: any WalletPageServing, refresh: Bool) async {
        if transactionLoading && !refresh { return }
        let targetPage = refresh ? 1 : transactionPage
        let previousPage = transactionPage
        let previousCanLoadMore = transactionCanLoadMore
        let range = dateRange(for: filter)
        transactionLoading = true
        transactionRefreshing = refresh
        errorMessage = nil
        if refresh {
            transactionCanLoadMore = true
            transactionPage = 1
        }
        notifyStateChange()

        do {
            let response = try await api.earningDetail(
                startDate: range.start,
                endDate: range.end,
                page: targetPage,
                pageSize: pageSize
            )
            transactionGroups = refresh ? response.list : transactionGroups + response.list
            totalIncomeLabel = Self.formatAmount(response.totalAmount)
            totalPointsLabel = response.totalPoints
            let itemCount = transactionGroups.reduce(0) { $0 + $1.items.count }
            transactionCanLoadMore = itemCount < response.total
            transactionPage = response.list.isEmpty ? targetPage : targetPage + 1
        } catch {
            if refresh {
                transactionPage = previousPage
                transactionCanLoadMore = previousCanLoadMore
            }
            errorMessage = error.localizedDescription
        }

        transactionLoading = false
        transactionRefreshing = false
        notifyStateChange()
    }

    private func dateRange(for filter: WalletFilter) -> (start: String, end: String) {
        let now = nowProvider()
        let end = calendar.startOfDay(for: now)
        let start = calendar.date(byAdding: .day, value: -filter.dayOffset, to: end) ?? end
        return (Self.dayFormatter.string(from: start), Self.dayFormatter.string(from: end))
    }

    private func notifyStateChange() {
        onStateChange?()
    }

    /// 格式化人民币金额。
    static func formatAmount(_ value: String?) -> String {
        guard let value = value?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty else {
            return "¥ 0.00"
        }
        guard let decimal = Decimal(string: value) else { return "¥ \(value)" }
        let number = NSDecimalNumber(decimal: decimal)
        let formatter = NumberFormatter()
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2
        formatter.roundingMode = .halfUp
        return "¥ \(formatter.string(from: number) ?? value)"
    }

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

/// 钱包收益明细列表扁平条目。
enum WalletTransactionEntry: Hashable {
    case header(EarningDetailGroup)
    case item(EarningDetailItem)
}

/// 钱包记录状态的语义色调，由页面映射为模块内固定色值。
enum WalletStatusTone: Equatable {
    case info
    case success
    case warning
    case danger
}

/// 银行卡提现记录的状态展示映射，对齐 Android 结算进度和时间线可见性。
struct WalletWithdrawStatusDisplay: Equatable {
    let tone: WalletStatusTone
    let progress: Int
    let showsTimeline: Bool

    /// 根据后端结算状态创建展示数据。
    init(status: Int) {
        switch status {
        case 20:
            tone = .success
            progress = 80
            showsTimeline = true
        case 30:
            tone = .danger
            progress = 100
            showsTimeline = true
        case 40:
            tone = .warning
            progress = 40
            showsTimeline = true
        case 50:
            tone = .success
            progress = 100
            showsTimeline = false
        case 60:
            tone = .danger
            progress = 0
            showsTimeline = true
        default:
            tone = .info
            progress = status == 10 ? 10 : 100
            showsTimeline = true
        }
    }
}

/// 积分兑现记录的状态文案、进度与时间字段映射。
struct PointWithdrawStatusDisplay: Equatable {
    let title: String
    let tone: WalletStatusTone
    let progress: Int
    let timeTitle: String

    /// 根据后端积分提现状态创建展示数据。
    init(status: Int) {
        switch status {
        case 1:
            title = "提现中"
            tone = .warning
            progress = 40
            timeTitle = "预计到账时间："
        case 2:
            title = "提现成功"
            tone = .success
            progress = 0
            timeTitle = "到账时间："
        case 3:
            title = "提现失败"
            tone = .danger
            progress = 100
            timeTitle = "审核时间："
        default:
            title = "未知"
            tone = .info
            progress = 0
            timeTitle = "预计到账时间："
        }
    }
}

/// 提现申请 ViewModel，管理金额、短信验证码、倒计时和提交状态。
final class WithdrawViewModel {
    private(set) var withdrawInfo: WithdrawInfoResponse?
    private(set) var amount = ""
    private(set) var verificationCode = ""
    private(set) var countdown = 0
    private(set) var loading = false
    private(set) var submitting = false
    private(set) var statusMessage: String?
    private(set) var errorMessage: String?

    var onStateChange: (() -> Void)?

    /// 加载提现申请页信息。
    func load(api: any WalletPageServing) async {
        loading = true
        errorMessage = nil
        notifyStateChange()
        do {
            withdrawInfo = try await api.withdrawInfo()
        } catch {
            errorMessage = "获取提现信息失败：\(error.localizedDescription)"
        }
        loading = false
        notifyStateChange()
    }

    /// 更新提现金额。
    func updateAmount(_ value: String) {
        amount = Self.normalizedMoneyInput(value, previous: amount)
        notifyStateChange()
    }

    /// 填入全部可提现金额。
    func withdrawAll() {
        amount = withdrawInfo?.amountWithdrawable ?? ""
        notifyStateChange()
    }

    /// 更新短信验证码。
    func updateVerificationCode(_ value: String) {
        verificationCode = value
        notifyStateChange()
    }

    /// 发送提现短信验证码。
    func requestVerificationCode(api: any WalletPageServing) async {
        guard countdown == 0 else { return }
        do {
            try await api.withdrawSendSMS()
            statusMessage = "验证码发送成功"
            countdown = 60
        } catch {
            errorMessage = "验证码发送失败：\(error.localizedDescription)"
        }
        notifyStateChange()
    }

    /// 倒计时减少一秒，由页面定时器驱动。
    func decrementCountdown() {
        guard countdown > 0 else { return }
        countdown -= 1
        notifyStateChange()
    }

    /// 提交提现申请。
    func submit(api: any WalletPageServing) async -> Bool {
        if let validationMessage {
            errorMessage = validationMessage
            notifyStateChange()
            return false
        }
        submitting = true
        notifyStateChange()
        do {
            try await api.withdrawApply(amount: amount, smsCode: verificationCode)
            statusMessage = "已提交申请"
            submitting = false
            notifyStateChange()
            return true
        } catch {
            errorMessage = error.localizedDescription
            submitting = false
            notifyStateChange()
            return false
        }
    }

    /// 表单是否可提交。
    var canSubmit: Bool {
        withdrawInfo != nil &&
            !amount.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
            !verificationCode.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
            !submitting
    }

    /// 表单校验提示。
    var validationMessage: String? {
        guard let withdrawInfo else { return "提现信息加载中" }
        guard !amount.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return "请输入提现金额" }
        guard let amountDecimal = Decimal(string: amount) else { return "请输入有效的金额" }
        let withdrawable = Decimal(string: withdrawInfo.amountWithdrawable) ?? 0
        if amountDecimal > withdrawable { return "金额不能大于可提现金额" }
        guard !verificationCode.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return "请输入验证码" }
        return nil
    }

    /// 规范化金额输入，最多保留两位小数。
    static func normalizedMoneyInput(_ value: String, previous: String) -> String {
        var result = ""
        var hasDot = false
        var decimalCount = 0
        for character in value {
            if character.isNumber {
                if hasDot {
                    guard decimalCount < 2 else { continue }
                    decimalCount += 1
                }
                result.append(character)
            } else if character == ".", !hasDot {
                hasDot = true
                if result.isEmpty { result = "0" }
                result.append(character)
            }
        }
        while result.count > 1, result.first == "0", result.dropFirst().first?.isNumber == true {
            result.removeFirst()
        }
        return result
    }

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

/// 积分兑现 ViewModel，管理积分概览、积分提现吗申请和记录分页。
final class PointsRedemptionViewModel {
    private(set) var overview: PointOverviewResponse?
    private(set) var withdrawnPoints = 0
    private(set) var pointsInput = ""
    private(set) var withdrawRecords: [PointWithdrawItem] = []
    private(set) var overviewLoading = false
    private(set) var withdrawLoading = false
    private(set) var withdrawRefreshing = false
    private(set) var statusMessage: String?
    private(set) var errorMessage: String?

    var onStateChange: (() -> Void)?

    private let staffIdProvider: () -> Int?
    private var withdrawPage = 1
    private var withdrawCanLoadMore = true
    private let pageSize = 10

    /// 初始化积分兑现 ViewModel。
    init(staffIdProvider: @escaping () -> Int? = {
        Int(AppStore.shared.session.userId.trimmingCharacters(in: .whitespacesAndNewlines))
    }) {
        self.staffIdProvider = staffIdProvider
    }

    /// 首次加载积分兑现页数据。
    func loadInitial(api: any WalletPageServing) async {
        await refreshOverview(api: api)
        await refreshWithdrawList(api: api)
    }

    /// 刷新积分概览。
    func refreshOverview(api: any WalletPageServing) async {
        guard let staffId = staffIdProvider(), staffId > 0 else { return }
        overviewLoading = true
        errorMessage = nil
        notifyStateChange()
        do {
            let response = try await api.pointOverview(staffId: staffId)
            overview = response
            withdrawnPoints = response.withdrawnPoints
        } catch {
            errorMessage = error.localizedDescription
        }
        overviewLoading = false
        notifyStateChange()
    }

    /// 刷新积分提现记录。
    func refreshWithdrawList(api: any WalletPageServing) async {
        await loadWithdrawList(api: api, refresh: true)
    }

    /// 滚动到底部时加载更多积分提现记录。
    func loadMoreWithdrawListIfNeeded(currentIndex: Int, api: any WalletPageServing) async {
        guard currentIndex >= withdrawRecords.count - 2, withdrawCanLoadMore, !withdrawLoading else { return }
        await loadWithdrawList(api: api, refresh: false)
    }

    /// 更新积分输入。
    func updatePointsInput(_ value: String) {
        let digits = value.filter(\.isNumber)
        guard !digits.isEmpty else {
            pointsInput = ""
            notifyStateChange()
            return
        }
        let points = min(Int(digits) ?? 0, withdrawnPoints)
        pointsInput = String(points)
        notifyStateChange()
    }

    /// 填入全部可提现吗积分。
    func withdrawAll() {
        pointsInput = String(withdrawnPoints)
        notifyStateChange()
    }

    /// 提交积分提现申请。
    func applyWithdraw(api: any WalletPageServing) async {
        let points = Int(pointsInput) ?? 0
        guard points > 0 else {
            errorMessage = "请输入提现积分"
            notifyStateChange()
            return
        }
        guard points <= withdrawnPoints else {
            errorMessage = "提现积分不能大于可提现积分"
            notifyStateChange()
            return
        }
        do {
            try await api.pointWithdrawApply(points: points, remark: "积分提现申请")
            statusMessage = "提现申请成功"
            pointsInput = ""
            notifyStateChange()
            await refreshOverview(api: api)
            await refreshWithdrawList(api: api)
        } catch {
            errorMessage = error.localizedDescription.isEmpty ? "提现申请失败" : error.localizedDescription
            notifyStateChange()
        }
    }

    private func loadWithdrawList(api: any WalletPageServing, refresh: Bool) async {
        if withdrawLoading && !refresh { return }
        let targetPage = refresh ? 1 : withdrawPage
        let previousPage = withdrawPage
        let previousCanLoadMore = withdrawCanLoadMore
        withdrawLoading = true
        withdrawRefreshing = refresh
        errorMessage = nil
        if refresh {
            withdrawCanLoadMore = true
            withdrawPage = 1
        }
        notifyStateChange()

        do {
            let response = try await api.pointWithdrawList(status: nil, page: targetPage, pageSize: pageSize)
            withdrawRecords = refresh ? response.list : withdrawRecords + response.list
            withdrawCanLoadMore = withdrawRecords.count < response.total
            withdrawPage = response.list.isEmpty ? targetPage : targetPage + 1
        } catch {
            if refresh {
                withdrawPage = previousPage
                withdrawCanLoadMore = previousCanLoadMore
            }
            errorMessage = error.localizedDescription
        }

        withdrawLoading = false
        withdrawRefreshing = false
        notifyStateChange()
    }

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