//
//  WalletViewController.swift
//  suixinkan
//

import SnapKit
import UIKit

/// 我的钱包页面，对齐 Android 钱包首页的余额卡、提现记录、收益明细和提现入口。
final class WalletViewController: BaseViewController, UITableViewDelegate {
    private enum Section {
        case main
    }

    private enum Item: Hashable {
        case withdraw(WalletWithdrawItem)
        case transactionHeader(EarningDetailGroup)
        case transaction(EarningDetailItem)
        case loading
    }

    private let viewModel: WalletViewModel
    private let walletAPI: any WalletPageServing
    private let profileAPI: any WalletProfileServing

    private let summaryCard = WalletSummaryCardView()
    private let contentCard = UIView()
    private let tabStack = UIStackView()
    private let withdrawTabButton = UIButton(type: .system)
    private let transactionTabButton = UIButton(type: .system)
    private let tabIndicator = UIView()
    private let filterPanel = WalletFilterPanelView()
    private let tableView = UITableView(frame: .zero, style: .plain)
    private let refreshControl = UIRefreshControl()
    private let bottomBar = UIView()
    private let withdrawButton = AppButton(title: "提现到银行卡")

    private var dataSource: UITableViewDiffableDataSource<Section, Item>!
    private var lastShownMessage: String?

    /// 初始化钱包页面。
    init(
        viewModel: WalletViewModel = WalletViewModel(),
        walletAPI: (any WalletPageServing)? = nil,
        profileAPI: (any WalletProfileServing)? = nil
    ) {
        self.viewModel = viewModel
        self.walletAPI = walletAPI ?? NetworkServices.shared.walletAPI
        self.profileAPI = profileAPI ?? NetworkServices.shared.profileAPI
        super.init(nibName: nil, bundle: nil)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func setupNavigationBar() {
        let titleLabel = UILabel()
        titleLabel.text = "我的钱包"
        titleLabel.textColor = WalletTokens.text333
        titleLabel.font = UIFont.systemFont(ofSize: 18)
        navigationItem.titleView = titleLabel
    }

    override func setupUI() {
        view.backgroundColor = WalletTokens.pageBackground
        contentCard.backgroundColor = .white
        contentCard.layer.cornerRadius = WalletTokens.contentRadius
        contentCard.clipsToBounds = true

        tabStack.axis = .horizontal
        tabStack.distribution = .fillEqually
        [withdrawTabButton, transactionTabButton].forEach {
            $0.titleLabel?.font = WalletTokens.tabSelectedFont
            $0.setTitleColor(WalletTokens.tabNormal, for: .normal)
            tabStack.addArrangedSubview($0)
        }
        withdrawTabButton.setTitle("提现记录", for: .normal)
        transactionTabButton.setTitle("收益明细", for: .normal)
        tabIndicator.backgroundColor = WalletTokens.summaryBackground
        tabIndicator.layer.cornerRadius = 1.5

        tableView.backgroundColor = .white
        tableView.separatorStyle = .none
        tableView.delegate = self
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 120
        tableView.refreshControl = refreshControl
        tableView.contentInset = UIEdgeInsets(top: WalletTokens.rowGap, left: 0, bottom: WalletTokens.rowGap, right: 0)
        tableView.verticalScrollIndicatorInsets = tableView.contentInset
        tableView.register(WalletWithdrawRecordCell.self, forCellReuseIdentifier: WalletWithdrawRecordCell.reuseIdentifier)
        tableView.register(WalletTransactionHeaderCell.self, forCellReuseIdentifier: WalletTransactionHeaderCell.reuseIdentifier)
        tableView.register(WalletTransactionRecordCell.self, forCellReuseIdentifier: WalletTransactionRecordCell.reuseIdentifier)
        tableView.register(WalletLoadingCell.self, forCellReuseIdentifier: WalletLoadingCell.reuseIdentifier)

        bottomBar.backgroundColor = .white

        view.addSubview(summaryCard)
        view.addSubview(contentCard)
        contentCard.addSubview(tabStack)
        contentCard.addSubview(tabIndicator)
        contentCard.addSubview(filterPanel)
        contentCard.addSubview(tableView)
        view.addSubview(bottomBar)
        bottomBar.addSubview(withdrawButton)

        configureDataSource()
    }

    override func setupConstraints() {
        summaryCard.snp.makeConstraints { make in
            make.top.equalTo(view.safeAreaLayoutGuide).offset(WalletTokens.screenInset)
            make.leading.trailing.equalToSuperview().inset(WalletTokens.screenInset)
        }
        bottomBar.snp.makeConstraints { make in
            make.leading.trailing.bottom.equalToSuperview()
        }
        withdrawButton.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(WalletTokens.screenInset)
            make.leading.trailing.equalToSuperview().inset(WalletTokens.screenInset)
            make.bottom.equalTo(view.safeAreaLayoutGuide).inset(22)
        }
        withdrawButton.layer.cornerRadius = WalletTokens.bottomButtonRadius
        contentCard.snp.makeConstraints { make in
            make.top.equalTo(summaryCard.snp.bottom).offset(WalletTokens.cardGap)
            make.leading.trailing.equalToSuperview().inset(WalletTokens.screenInset)
            make.bottom.equalTo(bottomBar.snp.top).offset(-WalletTokens.cardGap)
        }
        tabStack.snp.makeConstraints { make in
            make.top.equalToSuperview()
            make.leading.trailing.equalToSuperview().inset(32)
            make.height.equalTo(50)
        }
        tabIndicator.snp.makeConstraints { make in
            make.width.equalTo(24)
            make.height.equalTo(3)
            make.bottom.equalTo(tabStack)
            make.centerX.equalTo(withdrawTabButton)
        }
        filterPanel.snp.makeConstraints { make in
            make.top.equalTo(tabStack.snp.bottom)
            make.leading.trailing.equalToSuperview().inset(WalletTokens.screenInset)
            make.height.equalTo(0)
        }
        tableView.snp.makeConstraints { make in
            make.top.equalTo(filterPanel.snp.bottom).offset(WalletTokens.rowGap)
            make.leading.trailing.bottom.equalToSuperview()
        }
    }

    override func bindActions() {
        viewModel.onStateChange = { [weak self] in
            Task { @MainActor in
                self?.applyState()
            }
        }
        summaryCard.onPointsTap = { [weak self] in
            self?.openPointsRedemption()
        }
        withdrawTabButton.addTarget(self, action: #selector(withdrawTabTapped), for: .touchUpInside)
        transactionTabButton.addTarget(self, action: #selector(transactionTabTapped), for: .touchUpInside)
        filterPanel.onFilterSelected = { [weak self] filter in
            guard let self else { return }
            Task { await self.viewModel.selectFilter(filter, api: self.walletAPI) }
        }
        refreshControl.addTarget(self, action: #selector(refreshPulled), for: .valueChanged)
        withdrawButton.addTarget(self, action: #selector(withdrawTapped), for: .touchUpInside)

        Task { await viewModel.refreshSummary(api: walletAPI) }
        Task { await viewModel.refreshWithdraw(api: walletAPI) }
        Task { await viewModel.refreshTransaction(api: walletAPI) }
        Task { await viewModel.refreshPoints(api: walletAPI) }
    }

    private func configureDataSource() {
        dataSource = UITableViewDiffableDataSource<Section, Item>(tableView: tableView) { tableView, indexPath, item in
            switch item {
            case .withdraw(let record):
                let cell = tableView.dequeueReusableCell(
                    withIdentifier: WalletWithdrawRecordCell.reuseIdentifier,
                    for: indexPath
                ) as? WalletWithdrawRecordCell
                cell?.apply(record)
                return cell ?? UITableViewCell()
            case .transactionHeader(let group):
                let cell = tableView.dequeueReusableCell(
                    withIdentifier: WalletTransactionHeaderCell.reuseIdentifier,
                    for: indexPath
                ) as? WalletTransactionHeaderCell
                cell?.apply(group)
                return cell ?? UITableViewCell()
            case .transaction(let record):
                let cell = tableView.dequeueReusableCell(
                    withIdentifier: WalletTransactionRecordCell.reuseIdentifier,
                    for: indexPath
                ) as? WalletTransactionRecordCell
                cell?.apply(record)
                return cell ?? UITableViewCell()
            case .loading:
                return tableView.dequeueReusableCell(
                    withIdentifier: WalletLoadingCell.reuseIdentifier,
                    for: indexPath
                )
            }
        }
    }

    private func applyState() {
        summaryCard.apply(
            withdrawable: viewModel.summary?.amountWithdrawable,
            total: viewModel.summary?.amountTotal,
            balance: viewModel.summary?.amountCurrentBalance,
            points: viewModel.availablePoints
        )
        applyTabAppearance()
        updateContentLayout()
        filterPanel.apply(filter: viewModel.filter, totalIncome: viewModel.totalIncomeLabel, totalPoints: viewModel.totalPointsLabel)
        let isRefreshing = switch viewModel.selectedTab {
        case .withdraw: viewModel.withdrawRefreshing
        case .transaction: viewModel.transactionRefreshing
        }
        if !isRefreshing {
            refreshControl.endRefreshing()
        }
        applySnapshot()
        if let message = viewModel.errorMessage, message != lastShownMessage {
            lastShownMessage = message
            showToast(message)
        }
    }

    private func applyTabAppearance() {
        let selectedColor = WalletTokens.summaryBackground
        let normalColor = WalletTokens.tabNormal
        withdrawTabButton.setTitleColor(viewModel.selectedTab == .withdraw ? selectedColor : normalColor, for: .normal)
        transactionTabButton.setTitleColor(viewModel.selectedTab == .transaction ? selectedColor : normalColor, for: .normal)
        withdrawTabButton.titleLabel?.font = viewModel.selectedTab == .withdraw ? WalletTokens.tabSelectedFont : WalletTokens.tabFont
        transactionTabButton.titleLabel?.font = viewModel.selectedTab == .transaction ? WalletTokens.tabSelectedFont : WalletTokens.tabFont
        tabIndicator.snp.remakeConstraints { make in
            make.width.equalTo(24)
            make.height.equalTo(3)
            make.bottom.equalTo(tabStack)
            make.centerX.equalTo(viewModel.selectedTab == .withdraw ? withdrawTabButton : transactionTabButton)
        }
        UIView.animate(withDuration: 0.2) {
            self.contentCard.layoutIfNeeded()
        }
    }

    private func updateContentLayout() {
        let showsFilter = viewModel.selectedTab == .transaction
        filterPanel.isHidden = !showsFilter
        filterPanel.snp.remakeConstraints { make in
            make.leading.trailing.equalToSuperview().inset(WalletTokens.screenInset)
            if showsFilter {
                make.top.equalTo(tabStack.snp.bottom).offset(WalletTokens.rowGap)
            } else {
                make.top.equalTo(tabStack.snp.bottom)
                make.height.equalTo(0)
            }
        }
    }

    private func applySnapshot() {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.main])
        if viewModel.selectedTab == .withdraw {
            snapshot.appendItems(viewModel.withdrawRecords.map(Item.withdraw), toSection: .main)
            if viewModel.withdrawLoading {
                snapshot.appendItems([.loading], toSection: .main)
            }
        } else {
            let items = viewModel.transactionEntries.map { entry -> Item in
                switch entry {
                case .header(let group): .transactionHeader(group)
                case .item(let item): .transaction(item)
                }
            }
            snapshot.appendItems(items, toSection: .main)
            if viewModel.transactionLoading {
                snapshot.appendItems([.loading], toSection: .main)
            }
        }
        dataSource.apply(snapshot, animatingDifferences: true)
        updateEmptyState()
    }

    private func updateEmptyState() {
        let emptyText: String?
        if viewModel.selectedTab == .withdraw, viewModel.withdrawRecords.isEmpty, !viewModel.withdrawLoading {
            emptyText = "暂无提现记录"
        } else if viewModel.selectedTab == .transaction, viewModel.transactionEntries.isEmpty, !viewModel.transactionLoading {
            emptyText = "暂无收益明细"
        } else {
            emptyText = nil
        }
        guard let emptyText else {
            tableView.backgroundView = nil
            return
        }
        let label = UILabel()
        label.text = emptyText
        label.textColor = WalletTokens.textDisabled
        label.font = WalletTokens.bodyFont
        label.textAlignment = .center
        tableView.backgroundView = label
    }

    private func openPointsRedemption() {
        navigationController?.pushViewController(PointsRedemptionViewController(), animated: true)
    }

    @objc private func withdrawTabTapped() {
        Task { await viewModel.selectTab(.withdraw, api: walletAPI) }
    }

    @objc private func transactionTabTapped() {
        Task { await viewModel.selectTab(.transaction, api: walletAPI) }
    }

    @objc private func refreshPulled() {
        Task { await viewModel.refreshSelectedTab(api: walletAPI) }
    }

    @objc private func withdrawTapped() {
        Task {
            showLoading()
            do {
                let destination = try await viewModel.resolveWithdrawDestination(profileAPI: profileAPI)
                hideLoading()
                route(to: destination)
            } catch {
                hideLoading()
                showToast(error.localizedDescription)
            }
        }
    }

    private func route(to destination: WalletWithdrawDestination) {
        switch destination {
        case .realNameAuth:
            navigationController?.pushViewController(RealNameAuthViewController(), animated: true)
        case .realNameAudit(let info):
            showToast("实名认证不通过，请重新提交")
            navigationController?.pushViewController(RealNameAuthAuditViewController(info: info), animated: true)
        case .realNamePending:
            showToast("实名认证审核中，请审核通过后再试")
        case .withdrawalSettings:
            navigationController?.pushViewController(WithdrawalSettingsViewController(), animated: true)
        case .withdrawalAudit(let bankCard):
            showToast("银行卡审核不通过，请重新提交")
            navigationController?.pushViewController(WithdrawalSettingsAuditViewController(bankCard: bankCard), animated: true)
        case .bankCardPending:
            showToast("银行卡审核中，请审核通过后再试")
        case .withdraw:
            let controller = WithdrawViewController()
            controller.onSubmitSuccess = { [weak self] in
                guard let self else { return }
                Task {
                    await self.viewModel.refreshSummary(api: self.walletAPI)
                    await self.viewModel.refreshWithdraw(api: self.walletAPI)
                }
            }
            navigationController?.pushViewController(controller, animated: true)
        }
    }

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        Task {
            switch viewModel.selectedTab {
            case .withdraw:
                await viewModel.loadMoreWithdrawIfNeeded(currentIndex: indexPath.row, api: walletAPI)
            case .transaction:
                await viewModel.loadMoreTransactionIfNeeded(currentIndex: indexPath.row, api: walletAPI)
            }
        }
    }
}

/// 钱包顶部金额汇总卡片。
private final class WalletSummaryCardView: UIView {
    var onPointsTap: (() -> Void)?

    private let withdrawableTitle = UILabel()
    private let withdrawableLabel = UILabel()
    private let totalView = WalletSummaryMetricView()
    private let balanceView = WalletSummaryMetricView()
    private let pointsView = WalletSummaryMetricView(showsArrow: true)
    private let metricsStack = UIStackView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(withdrawable: String?, total: String?, balance: String?, points: Int) {
        withdrawableLabel.text = WalletViewModel.formatAmount(withdrawable)
        totalView.apply(title: "累计金额", value: WalletViewModel.formatAmount(total))
        balanceView.apply(title: "当前余额", value: WalletViewModel.formatAmount(balance))
        pointsView.apply(title: "当前积分", value: "\(points)")
    }

    private func setupUI() {
        backgroundColor = WalletTokens.summaryBackground
        layer.cornerRadius = WalletTokens.summaryRadius
        clipsToBounds = true

        withdrawableTitle.text = "可提现金额"
        withdrawableTitle.textColor = UIColor.white.withAlphaComponent(0.8)
        withdrawableTitle.font = WalletTokens.bodyFont
        withdrawableTitle.textAlignment = .center

        withdrawableLabel.textColor = .white
        withdrawableLabel.font = WalletTokens.displayFont
        withdrawableLabel.textAlignment = .center
        withdrawableLabel.adjustsFontSizeToFitWidth = true
        withdrawableLabel.minimumScaleFactor = 0.7

        metricsStack.axis = .horizontal
        metricsStack.distribution = .fillEqually
        [totalView, balanceView, pointsView].forEach(metricsStack.addArrangedSubview)
        pointsView.addTarget(self, action: #selector(pointsTapped), for: .touchUpInside)

        addSubview(withdrawableTitle)
        addSubview(withdrawableLabel)
        addSubview(metricsStack)

        withdrawableTitle.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(WalletTokens.cardPadding)
            make.leading.trailing.equalToSuperview().inset(WalletTokens.summaryHorizontalPadding)
        }
        withdrawableLabel.snp.makeConstraints { make in
            make.top.equalTo(withdrawableTitle.snp.bottom).offset(WalletTokens.tinyGap)
            make.leading.trailing.equalToSuperview().inset(WalletTokens.summaryHorizontalPadding)
        }
        metricsStack.snp.makeConstraints { make in
            make.top.equalTo(withdrawableLabel.snp.bottom).offset(18)
            make.leading.trailing.equalToSuperview().inset(WalletTokens.summaryHorizontalPadding)
            make.bottom.equalToSuperview().inset(WalletTokens.cardPadding)
        }
    }

    @objc private func pointsTapped() {
        onPointsTap?()
    }
}

/// 钱包汇总卡片里的单项指标。
private final class WalletSummaryMetricView: UIControl {
    private let titleLabel = UILabel()
    private let valueLabel = UILabel()
    private let arrowLabel = UILabel()
    private let showsArrow: Bool

    init(showsArrow: Bool = false) {
        self.showsArrow = showsArrow
        super.init(frame: .zero)
        setupUI()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(title: String, value: String) {
        titleLabel.text = title
        valueLabel.text = value
    }

    private func setupUI() {
        titleLabel.textColor = UIColor.white.withAlphaComponent(0.8)
        titleLabel.font = WalletTokens.bodyFont
        titleLabel.textAlignment = .center

        valueLabel.textColor = .white
        valueLabel.font = WalletTokens.bodyMediumFont
        valueLabel.textAlignment = .center
        valueLabel.adjustsFontSizeToFitWidth = true
        valueLabel.minimumScaleFactor = 0.75

        arrowLabel.text = showsArrow ? "›" : ""
        arrowLabel.textColor = .white
        arrowLabel.font = .systemFont(ofSize: 20, weight: .medium)

        let valueStack = UIStackView(arrangedSubviews: [valueLabel, arrowLabel])
        valueStack.axis = .horizontal
        valueStack.alignment = .center
        valueStack.spacing = 2
        valueStack.distribution = .fill

        addSubview(titleLabel)
        addSubview(valueStack)

        titleLabel.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview()
        }
        valueStack.snp.makeConstraints { make in
            make.top.equalTo(titleLabel.snp.bottom).offset(6)
            make.centerX.equalToSuperview()
            make.leading.greaterThanOrEqualToSuperview()
            make.trailing.lessThanOrEqualToSuperview()
            make.bottom.equalToSuperview()
        }
    }
}

/// 收益明细筛选和汇总面板。
private final class WalletFilterPanelView: UIView {
    var onFilterSelected: ((WalletFilter) -> Void)?

    private let filterButton = UIButton(type: .system)
    private let totalTitleLabel = UILabel()
    private let totalLabel = UILabel()
    private let pointsLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(filter: WalletFilter, totalIncome: String, totalPoints: Int) {
        filterButton.configuration?.title = filter.title
        configureMenu(selected: filter)
        totalLabel.text = totalIncome.isEmpty ? "¥ 0.00" : totalIncome
        pointsLabel.text = "积分+\(totalPoints)"
    }

    private func setupUI() {
        backgroundColor = WalletTokens.filterBackground
        layer.cornerRadius = WalletTokens.contentRadius
        clipsToBounds = true

        filterButton.setTitleColor(UIColor(hex: 0x111827), for: .normal)
        filterButton.titleLabel?.font = WalletTokens.bodyFont
        filterButton.contentHorizontalAlignment = .left
        filterButton.backgroundColor = WalletTokens.inputFocusedBackground
        filterButton.layer.cornerRadius = WalletTokens.contentRadius
        filterButton.showsMenuAsPrimaryAction = true
        filterButton.changesSelectionAsPrimaryAction = false
        filterButton.configuration = .plain()
        filterButton.configuration?.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 12)
        filterButton.configuration?.image = UIImage(systemName: "chevron.down")
        filterButton.configuration?.imagePlacement = .trailing
        filterButton.configuration?.imagePadding = 8
        filterButton.tintColor = WalletTokens.text333

        totalTitleLabel.text = "总收益:"
        totalTitleLabel.font = WalletTokens.bodyMediumFont
        totalTitleLabel.textColor = .black

        totalLabel.font = WalletTokens.bodyMediumFont
        totalLabel.textColor = WalletTokens.primary

        pointsLabel.font = WalletTokens.bodyMediumFont
        pointsLabel.textColor = WalletTokens.points
        pointsLabel.textAlignment = .right

        addSubview(filterButton)
        addSubview(totalTitleLabel)
        addSubview(totalLabel)
        addSubview(pointsLabel)

        filterButton.snp.makeConstraints { make in
            make.top.leading.equalToSuperview()
            make.width.lessThanOrEqualTo(WalletTokens.filterFieldMaxWidth)
            make.height.equalTo(WalletTokens.filterFieldHeight)
        }
        totalTitleLabel.snp.makeConstraints { make in
            make.top.equalTo(filterButton.snp.bottom)
            make.leading.equalToSuperview().offset(WalletTokens.cardPadding)
            make.bottom.equalToSuperview().inset(WalletTokens.rowGap)
        }
        totalLabel.snp.makeConstraints { make in
            make.centerY.equalTo(totalTitleLabel)
            make.leading.equalTo(totalTitleLabel.snp.trailing).offset(8)
        }
        pointsLabel.snp.makeConstraints { make in
            make.centerY.equalTo(totalTitleLabel)
            make.leading.greaterThanOrEqualTo(totalLabel.snp.trailing).offset(8)
            make.trailing.equalToSuperview().inset(16)
        }
    }

    private func configureMenu(selected: WalletFilter) {
        let actions = WalletFilter.allCases.map { [weak self] filter in
            UIAction(
                title: filter.title,
                state: filter == selected ? .on : .off
            ) { _ in
                self?.onFilterSelected?(filter)
            }
        }
        filterButton.menu = UIMenu(children: actions)
    }
}
