//
//  QuestionViewController.swift
//  RoderickRalph
//
//  Created by Neoa on 2025/8/19.
//

import Foundation
import UIKit
import AnyThinkBanner
import AnyThinkSDK
import AnyThinkInterstitial
import AnyThinkNative
import AnyThinkRewardedVideo


class QuizStageController: UIViewController, ATAdLoadingDelegate, ATBannerDelegate, ATInterstitialDelegate, ATNativeADDelegate, ATRewardedVideoDelegate, EnergyPanelAgent, OverlayPanelAgent{
    
    // MARK: - UI Components
    
    // 实际使用的广告placement ID（从接口获取或使用默认值）
    private var bannerPlacementID: String = "b68d88cc2ab33d"
    private var interstitialPlacementID: String = "b68d88cc36bca8"
    private var nativeRenderPlacementID: String = "b68d88cc1605f1"
    private var rewardPlacementID: String = "b68d88cc20bb14"

    private var nativeAdView: ATNativeADView?
    private var selfRenderView: RenderUnitView?
    private var nativeAdOffer: ATNativeAdOffer?

    private var nativeAdContainer: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .clear
        view.isUserInteractionEnabled = true
        return view
    }()

    private var bannerView: ATBannerView?
    private var bannerContainer: UIView = {
        let container = UIView()
        container.translatesAutoresizingMaskIntoConstraints = false
        container.backgroundColor = .clear
        container.isUserInteractionEnabled = true
        container.clipsToBounds = false // 确保子视图不会被裁剪
        return container
    }()
    private var bannerHeightConstraint: NSLayoutConstraint?
    private var bannerSize: CGSize {
        let screenWidth = UIScreen.main.bounds.width
        let aspectRatio: CGFloat = 320 / 50
        let bannerWidth = screenWidth
        let bannerHeight = bannerWidth / aspectRatio
        return CGSize(width: bannerWidth, height: bannerHeight)
    }

    // MARK: - Channel API (moved to LoginQuestionViewController)
    private let getDefaultConfig = "/wx/getByDitchId"
    private let getQuestionListPath = "/question/list"
    private let addPowerPath = "/wx/addPower"
    private let answerQuestionPath = "/question/answerQuestion"
    private let adSaveRecordPath = "/ad/saveRecord"

    // MARK: - Get Power Countdown
    private var powerCountdownOverlay: UIView?
    private var powerCountdownLabel: UILabel?
    private var powerCountdownTimer: Timer?
    private var powerRemainingSeconds: Int = 60
    private var didStartPowerCountdown = false

    // Remote-configured values with sensible defaults
    private var staminaWaitSec: Int = 60
    private var interIntervalSec: Int = 30
    private var interstitialTimer: Timer?
    private var nativeIntervalSec: Int = 30
    private var nativeTimer: Timer?
    private var isAllowNativeAutoRefresh: Bool = true
    
    // 1：不可叠加体力；0：可叠加体力
    private var canStackPower: Int = 0
    
    private var accountInfo: LoginUserInfo?
    private var selectedChannelRef: Channel?

    // Reward flow gating for cooldown
    private var rewardPowerOK: Bool = false
    private var rewardVideoClosed: Bool = false
    private var pendingToastText: String?
    
    // MARK: - Question Data State
    private struct QAItem { let id: String; let content: String }
    private struct QAQuestion { let id: String; let content: String; let correctItemId: String; let items: [QAItem] }
    private var questions: [QAQuestion] = []
    private var currentQuestionIndex: Int = 0
    private var option1ItemId: String?
    private var option2ItemId: String?
    private var questionStartAt: Date?
    private var lastQuestionIdFromLogin: String?
    private var isAnswering: Bool = false // 防止重复答题标志
    
    private let userInfoBGView: UIView = {
        let view = UIImageView()
        view.contentMode = .scaleToFill
        view.image = UIImage(named: "res_ogoxcw01")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private let iconImageView: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage(named: "catlogos")
        iv.contentMode = .scaleAspectFit
        iv.layer.cornerRadius = 29
        iv.clipsToBounds = true
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()


    
    private let userInfoLabel: UILabel = {
        let label = UILabel()
        label.text = ""
        label.font = UIFont.boldSystemFont(ofSize: 10)
        label.textColor = .white
        
        // 添加橘色的文字描边
        let strokeTextAttributes: [NSAttributedString.Key: Any] = [
            .strokeColor: UIColor.orange,  // 设置描边颜色
            .strokeWidth: -2.0,  // 设置描边宽度，负值为描边，正值为文字内部留白
            .foregroundColor: UIColor.white  // 设置文字颜色
        ]
        let attributedString = NSAttributedString(string: label.text ?? "", attributes: strokeTextAttributes)
        label.attributedText = attributedString
        
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let musiceView: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage(named: "res_fb2rjej5")
        iv.contentMode = .scaleToFill
//        iv.layer.cornerRadius = 29
//        iv.clipsToBounds = true
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()
    
    private let gainPowerBtn: UIButton = {
        let button = UIButton(type: .system)
        
        button.setBackgroundImage(UIImage(named: "res_4sw46mis"), for: .normal)
        button.setTitle("获取体力", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.contentHorizontalAlignment = .left
        button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0)  // Adjust left padding
        
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    private let todayCountLabel: UILabel = {
        let label = UILabel()
        label.text = "今日答题: 0题"
        label.font = UIFont.systemFont(ofSize: 13)
        label.textColor = .white
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let historyCountLabel: UILabel = {
        let label = UILabel()
        label.text = "历史答题: 0题"
        label.font = UIFont.systemFont(ofSize: 13)
        label.textColor = .white
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    private let verdictBGView: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage(named: "res_27q0oxly")
        iv.contentMode = .scaleToFill
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()

    private let verdictLabel: UILabel = {
        let label = UILabel()
        label.text = "请选择正确答案"
        label.font = UIFont.systemFont(ofSize: 24, weight: .bold)
        label.textAlignment = .center
        label.textColor = .white
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    private let boardBGView: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage(named: "res_b0fsh9ql")
        iv.contentMode = .scaleToFill
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()

    
    private let promptLabel: UILabel = {
        let label = UILabel()
        label.text = "《二泉映月》是首用什么乐器独奏的曲子"
        label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
        label.textColor = .black
        label.textAlignment = .center
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let levelBadge: UIButton = {
        let button = UIButton(type: .system)
        
        button.setBackgroundImage(UIImage(named: "res_fm0lbz7w"), for: .normal)
        button.setTitle("关卡:1", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize: 20)
        button.contentHorizontalAlignment = .center
        button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)  // Adjust left padding
        button.isUserInteractionEnabled = false
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    private let staminaBadge: UIButton = {
        let button = UIButton(type: .system)
        
        button.setBackgroundImage(UIImage(named: "res_fm0lbz7w"), for: .normal)
        button.setTitle("体力:0", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize: 20)
        button.contentHorizontalAlignment = .center
        button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        button.isUserInteractionEnabled = false
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()


    
    private let choiceBtnA: UIButton = {
        let button = UIButton(type: .system)
        button.setBackgroundImage(UIImage(named: "res_o67j6d3t"), for: .normal)
        button.setTitle("二胡", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 24)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(optionButtonTapped(_:)), for: .touchUpInside)
        return button
    }()
    
    private let choiceBtnB: UIButton = {
        let button = UIButton(type: .system)
        button.setBackgroundImage(UIImage(named: "res_o67j6d3t"), for: .normal)
        button.setTitle("钢琴", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 24)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(optionButtonTapped(_:)), for: .touchUpInside)
        return button
    }()
    
    private let markA: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage(named: "res_cwqxdp40")
        iv.contentMode = .scaleAspectFill
        iv.isHidden = true
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()
    
    private let markB: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage(named: "res_4h4cvu8h")
        iv.contentMode = .scaleToFill
        iv.isHidden = true
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()


    
    // MARK: - Public Methods for Data Transfer from LoginQuestionViewController
    
    func setLoginUserInfo(_ userInfo: LoginUserInfo) {
        self.accountInfo = userInfo
        
        // 如果界面已经加载，立即更新UI
        if isViewLoaded {
            updateUIWithUserInfo(userInfo)
        }
    }
    
    func setSelectedChannel(_ channel: Channel) {
        self.selectedChannelRef = channel
        
    }
    
    private func updateUIWithUserInfo(_ userInfo: LoginUserInfo) {
        // 更新右侧统计
        todayCountLabel.text = "今日答题: \(userInfo.todayAnswerCount)题"
        historyCountLabel.text = "历史答题: \(userInfo.historyAnswerCount)题"
        userInfoLabel.text = userInfo.userId
        setIconImage(with: userInfo.headImgURL)
        
        // 更新体力显示
        let savedPower = UserDefaults.standard.object(forKey: "power") as? Int ?? 0
        staminaBadge.setTitle("体力:\(savedPower)", for: .normal)
        applyPowerToOptionButtons(savedPower)
    }
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
//        navigationController?.setNavigationBarHidden(true, animated: false)

        // 先不加载广告，等接口返回配置后再加载
        // 这样可以确保使用正确的placement ID
        
        // Set up gestures for both the icon and the label
        let iconTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleIconTap))
        iconImageView.isUserInteractionEnabled = true
        iconImageView.addGestureRecognizer(iconTapGesture)
        
        let labelTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleLabelTap))
        userInfoLabel.isUserInteractionEnabled = true
        userInfoLabel.addGestureRecognizer(labelTapGesture)
        
        gainPowerBtn.addTarget(self, action: #selector(handleGetPowerButtonTap), for: .touchUpInside)
        
        setupUI()
        setupConstraints()

        // Prefill icon with saved avatar (headImgURL) or placeholder
        if let userInfo = accountInfo {
            // 使用传入的用户信息更新UI
            updateUIWithUserInfo(userInfo)
        } else {
            // 使用本地保存的数据
            let savedHead = UserDefaults.standard.string(forKey: "headImgURL")
            setIconImage(with: savedHead)
            
            // Prefill power and gray out options if needed
            let savedPower = UserDefaults.standard.object(forKey: "power") as? Int ?? 0
            staminaBadge.setTitle("体力:\(savedPower)", for: .normal)
            applyPowerToOptionButtons(savedPower)
        }

        // Channel selection is now handled in LoginQuestionViewController
        // Fetch questions directly since login is complete
        fetchQuestionList()
        
        
        fetchDefaultConfig(channel: self.selectedChannelRef!)

        
        //刷新一次banner
        DispatchQueue.main.asyncAfter(deadline: .now() + 7) { [weak self] in
            guard let self = self else { return }
            self.removeBanner()
            self.loadBannerAd()
        }

    }
    

    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.setNavigationBarHidden(true, animated: animated)
        
        // 监听应用进入前台的通知
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(appDidEnterForeground),
            name: UIApplication.willEnterForegroundNotification,
            object: nil
        )
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // 移除通知监听
        NotificationCenter.default.removeObserver(
            self,
            name: UIApplication.willEnterForegroundNotification,
            object: nil
        )
    }

    private func loadNativeAd() {
        let extra: [AnyHashable: Any] = [
            kATAdLoadingExtraMediaExtraKey: "QuestionVC",
            kATExtraInfoNativeAdSizeKey: NSValue(cgSize: CGSize(width: UIScreen.main.bounds.width - 24, height: 170))
        ]
        
        // Load the native ad with placement ID
        ATAdManager.shared().loadAD(withPlacementID: nativeRenderPlacementID, extra: extra, delegate: self)
    }

    private func showNativeAdIfReady() {
        // 检查广告是否准备好
        guard ATAdManager.shared().nativeAdReady(forPlacementID: nativeRenderPlacementID) else {
            print("Native ad not ready")
            return
        }
        
        // 获取广告offer
        guard let offer = ATAdManager.shared().getNativeAdOffer(withPlacementID: nativeRenderPlacementID) else {
            print("Failed to get native ad offer")
            return
        }
        
        self.nativeAdOffer = offer
        
        // 创建自渲染视图
        let selfRenderView = RenderUnitView(offer: offer)
        self.selfRenderView = selfRenderView
        
        // 初始化配置
        let config = ATNativeADConfiguration()
        config.adFrame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 170)
        config.mediaViewFrame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 150)
        config.delegate = self
        config.rootViewController = self
        config.sizeToFit = true
        config.videoPlayType = .alwaysAutoPlayType
        
        // 设置logo位置
        config.logoViewFrame = CGRect(x: UIScreen.main.bounds.width - 48, y: 140, width: 36, height: 20)
        
        // 创建原生广告视图
        let nativeAdView = ATNativeADView(configuration: config, currentOffer: offer, placementID: nativeRenderPlacementID)
        self.nativeAdView = nativeAdView
        
        // 获取mediaView
        if let mediaView = nativeAdView.getMediaView() {
            selfRenderView.installMediaView(mediaView)
        }
        
        // 创建可点击组件数组
        var clickableViewArray: [UIView] = []
        clickableViewArray.append(contentsOf: [
//            selfRenderView.iconImageView,
            selfRenderView.headlineLabel,
            selfRenderView.bodyLabel,
//            selfRenderView.ctaLabel,
            selfRenderView.heroImageView
        ])
        
        // 注册点击事件
        nativeAdView.registerClickableViewArray(clickableViewArray)
        
        // 绑定组件
        let info = ATNativePrepareInfo.load { prepareInfo in
            prepareInfo.textLabel = selfRenderView.bodyLabel
//            prepareInfo.advertiserLabel = selfRenderView.advertiserLabel
            prepareInfo.titleLabel = selfRenderView.headlineLabel
//            prepareInfo.ratingLabel = selfRenderView.ratingLabel
//            prepareInfo.iconImageView = selfRenderView.iconImageView
            prepareInfo.mainImageView = selfRenderView.heroImageView
//            prepareInfo.logoImageView = selfRenderView.logoImageView
//            prepareInfo.ctaLabel = selfRenderView.ctaLabel
            prepareInfo.dislikeButton = selfRenderView.closeControl
            if let mv = selfRenderView.mediaContainer {
                prepareInfo.mediaView = mv
            }
        }
        nativeAdView.prepare(with: info)
        
        // 渲染广告
        offer.renderer(with: config, selfRenderView: selfRenderView, nativeADView: nativeAdView)
        
        // 添加到容器（必须把 nativeAdView 放到视图层级中，点击与视频追踪才会生效）
        nativeAdContainer.subviews.forEach { $0.removeFromSuperview() }
        nativeAdContainer.addSubview(nativeAdView)
        nativeAdView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            nativeAdView.leadingAnchor.constraint(equalTo: nativeAdContainer.leadingAnchor),
            nativeAdView.trailingAnchor.constraint(equalTo: nativeAdContainer.trailingAnchor),
            nativeAdView.topAnchor.constraint(equalTo: nativeAdContainer.topAnchor),
            nativeAdView.bottomAnchor.constraint(equalTo: nativeAdContainer.bottomAnchor)
        ])
        // 自渲染视图放入 nativeAdView 内
        nativeAdView.addSubview(selfRenderView)
        selfRenderView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            selfRenderView.leadingAnchor.constraint(equalTo: nativeAdView.leadingAnchor),
            selfRenderView.trailingAnchor.constraint(equalTo: nativeAdView.trailingAnchor),
            selfRenderView.topAnchor.constraint(equalTo: nativeAdView.topAnchor),
            selfRenderView.bottomAnchor.constraint(equalTo: nativeAdView.bottomAnchor)
        ])
        
        print("Native self-render ad displayed successfully")
    }
    
    @objc func loadRewardAd() {

        var loadConfig: [String: Any] = [:]
        // 服务端激励验证透传参数（可选）
        loadConfig[kATAdLoadingExtraMediaExtraKey]  = "media_val_RewardedVC"
        loadConfig[kATAdLoadingExtraUserIDKey]      = "rv_test_user_id"
        loadConfig[kATAdLoadingExtraRewardNameKey]  = "reward_Name"
        loadConfig[kATAdLoadingExtraRewardAmountKey] = 1

        ATAdManager.shared().loadAD(withPlacementID: rewardPlacementID,
                                    extra: loadConfig,
                                    delegate: self)
    }

    // MARK: - Show Ad 展示广告
    @objc func showRewardAd() {
        // 就绪检查
        guard ATAdManager.shared().rewardedVideoReady(forPlacementID: rewardPlacementID) else {
            loadRewardAd()
            return
        }

        let config = ATShowConfig(scene: "", showCustomExt: "testShowCustomExt")

        // Reset reward flow state before showing a new video
        rewardPowerOK = false
        rewardVideoClosed = false
        // 注意：不清除pendingToastMessage，因为可能还有待显示的消息

        // 展示
        ATAdManager.shared().showRewardedVideo(withPlacementID: rewardPlacementID,
                                               config: config,
                                               in: self,
                                               delegate: self)
    }


    
    private func loadBannerAd() {
        var extra: [AnyHashable: Any] = [
            kATAdLoadingExtraMediaExtraKey: "QuestionVC"
        ]
        extra[kATAdLoadingExtraBannerAdSizeKey] = NSValue(cgSize: bannerSize)

        ATAdManager.shared().loadAD(withPlacementID: bannerPlacementID, extra: extra, delegate: self)
    }

    private func showBannerIfReady() {
        let config = ATShowConfig(scene: nil, showCustomExt: nil)
        guard let banner = ATAdManager.shared().retrieveBannerView(forPlacementID: bannerPlacementID, config: config) else { return }
        
        banner.delegate = self
        // 确保设置正确的 presentingViewController
        banner.presentingViewController = self
        banner.translatesAutoresizingMaskIntoConstraints = false
        
        bannerContainer.subviews.forEach { $0.removeFromSuperview() }
        bannerContainer.addSubview(banner)

        // Add constraints to the banner view
        NSLayoutConstraint.activate([
            banner.centerXAnchor.constraint(equalTo: bannerContainer.centerXAnchor),
            banner.topAnchor.constraint(equalTo: bannerContainer.topAnchor),
            banner.widthAnchor.constraint(equalToConstant: bannerSize.width),
            banner.heightAnchor.constraint(equalToConstant: bannerSize.height)
        ])

        // Store the banner view
        bannerView = banner
        bannerView?.isUserInteractionEnabled = true
        
        // Update the height constraint for the container
        bannerHeightConstraint?.constant = bannerSize.height
        UIView.animate(withDuration: 0.25) {
            self.view.layoutIfNeeded()
        }
        
        // 打印调试信息
        print("Banner frame: \(banner.frame)")
        print("Banner container frame: \(self.bannerContainer.frame)")
    }

    private func removeBanner() {
        bannerView?.destroyBanner()
        bannerView?.removeFromSuperview()
        bannerView = nil

        // Reset the height constraint to 0
        bannerHeightConstraint?.constant = 0
        UIView.animate(withDuration: 0.25) {
            self.view.layoutIfNeeded()
        }
    }
    
    // MARK: - ATAdLoadingDelegate
    func didFinishLoadingAD(withPlacementID placementID: String) {
        if placementID == bannerPlacementID {
            // 横幅广告加载完成，检查横幅广告是否准备好
            if ATAdManager.shared().bannerAdReady(forPlacementID: placementID) {
                showBannerIfReady()  // 展示横幅广告
            }
            print("Banner Ad Loaded: \(placementID)")
        } else if placementID == interstitialPlacementID {
            // 插屏广告加载完成，检查插屏广告是否准备好
            if ATAdManager.shared().interstitialReady(forPlacementID: placementID) {
                showInterstitialAd()  // 展示插屏广告
            }
            print("Interstitial Ad Loaded: \(placementID)")
        } else if placementID == nativeRenderPlacementID {
            if ATAdManager.shared().nativeAdReady(forPlacementID: placementID) {
                showNativeAdIfReady()
            }
            print("native Ad Loaded: \(placementID)")

        }
    }
    
    // MARK: - ATRewardedVideoDelegate（激励回调）
    // 激励成功（注意：原 ObjC 选择子为 PlacemenID，保持一致以兼容 SDK）
    func rewardedVideoDidRewardSuccess(forPlacemenID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoDidRewardSuccessForPlacemenID:\(placementID) extra:\(extra)")
        // 激励成功，向服务器申请体力
        addPower()
    }

    // 视频开始播放
    func rewardedVideoDidStartPlaying(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoDidStartPlayingForPlacementID:\(placementID) extra:\(extra)")
        adStartTimes[placementID] = nowMillis()
        incrementTodayAdCount()
    }

    // 视频播放结束
    func rewardedVideoDidEndPlaying(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoDidEndPlayingForPlacementID:\(placementID) extra:\(extra)")
    }

    // 视频播放失败（失败后预加载）
    func rewardedVideoDidFailToPlay(forPlacementID placementID: String, error: Error, extra: [AnyHashable: Any]) {
        let code = (error as NSError).code
        print("rewardedVideoDidFailToPlayForPlacementID:\(placementID) error:\(error) extra:\(extra)")

        // 预加载
        loadRewardAd()
    }

    // 广告关闭（关闭后预加载）
    @objc(rewardedVideoDidCloseForPlacementID:rewarded:extra:)
    func rewardedVideoDidClose(forPlacementID placementID: String, rewarded: Bool, extra: [AnyHashable: Any]) {
        print("rewardedVideoDidCloseForPlacementID:\(placementID), rewarded:\(rewarded) extra:\(extra)")

        // 预加载
        loadRewardAd()
        
        // 标记关闭并尝试开启倒计时
        rewardVideoClosed = true
        print("[Toast] Video closed, pending message: \(pendingToastText ?? "nil")")
        startCooldownIfRewardFlowDone()
        
        // 显示待显示的Toast消息
        showPendingToast()
    }

    // 广告点击
    @objc(rewardedVideoDidClickForPlacementID:extra:)
    func rewardedVideoDidClick(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoDidClickForPlacementID:\(placementID) extra:\(extra)")
    }

    // Deeplink/跳转
    @objc(rewardedVideoDidDeepLinkOrJumpForPlacementID:extra:result:)
    func rewardedVideoDidDeepLinkOrJump(forPlacementID placementID: String, extra: [AnyHashable: Any], result success: Bool) {
        print("rewardedVideoDidDeepLinkOrJumpForPlacementID:\(placementID) extra:\(extra) success:\(success)")
    }

    // MARK: - "再看一个"能力（Again）回调
    func rewardedVideoAgainDidRewardSuccess(forPlacemenID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoAgainDidRewardSuccessForPlacemenID:\(placementID) extra:\(extra)")
    }

    func rewardedVideoAgainDidStartPlaying(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoAgainDidStartPlayingForPlacementID:\(placementID) extra:\(extra)")
    }

    func rewardedVideoAgainDidEndPlaying(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoAgainDidEndPlayingForPlacementID:\(placementID) extra:\(extra)")
    }

    func rewardedVideoAgainDidFailToPlay(forPlacementID placementID: String, error: Error, extra: [AnyHashable: Any]) {
        let code = (error as NSError).code
        print("rewardedVideoAgainDidFailToPlayForPlacementID:\(placementID) error:\(error) extra:\(extra)")
    }

    func rewardedVideoAgainDidClick(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
        print("rewardedVideoAgainDidClickForPlacementID:\(placementID) extra:\(extra)")
    }

    
    // MARK: - ATNativeAdDelegate
    func didShowNativeAd(in adView: ATNativeADView, placementID: String, extra: [AnyHashable: Any]?) {
        print("Native ad shown for \(placementID)")
        adStartTimes[placementID] = nowMillis()
        incrementTodayAdCount()
        
        // 根据配置决定是否开始原生广告自动刷新计时
        if isAllowNativeAutoRefresh {
            startNativeAdRefreshTimer()
        } else {
            print("[Config] Native ad auto refresh is disabled")
        }
    }

    func didTapCloseButton(in adView: ATNativeADView, placementID: String, extra: [AnyHashable: Any]?) {
        print("Native ad closed for \(placementID)")
        removeNativeAd()
    }

    func didClickNativeAd(in adView: ATNativeADView, placementID: String, extra: [AnyHashable: Any]?) {
        print("Native ad clicked for \(placementID)")
    }

    
    func didFailToLoadAD(withPlacementID placementID: String, error: Error) {
        print("AD load failed(\(placementID)): \(error)")
        // 失败不影响主流程，保持 banner 容器高度为 0
        
        // banner广告失败，自动重试加载与展示
        if placementID == bannerPlacementID {
            print("[Banner] Load failed for bannerPlacementID, will retry...")
            // 清理旧视图以避免残留
            removeBanner()
            // 稍作延迟再重试，避免与 SDK 内部节流/并发冲突
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
                guard let self = self else { return }
                self.loadBannerAd()
            }
        }

        // 原生自渲染广告失败，自动重试加载与展示
        if placementID == nativeRenderPlacementID {
            print("[Native] Load failed for nativeRenderPlacementID, will retry...")
            // 清理旧视图以避免残留
            removeNativeAd()
            // 稍作延迟再重试，避免与 SDK 内部节流/并发冲突
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
                guard let self = self else { return }
                self.loadNativeAd()
            }
        }
    }

    func didRevenue(forPlacementID placementID: String, extra: [AnyHashable : Any]!) {
        // 可选：上报收益日志到我们服务器
        print("Banner revenue: placement=\(placementID), extra=\(extra ?? [:])")
        uploadAdRevenue(placementID: placementID, extra: extra ?? [:])
    }


    // MARK: - ATBannerDelegate
    func bannerView(_ bannerView: ATBannerView, didTapCloseButtonWithPlacementID placementID: String, extra: [AnyHashable: Any]!) {
        removeBanner()
    }

    func bannerView(_ bannerView: ATBannerView, didShowAdWithPlacementID placementID: String, extra: [AnyHashable: Any]!) {
        print("Banner Ad shown for \(placementID)")
        //展示广告的时间戳
        adStartTimes[placementID] = nowMillis()
        incrementTodayAdCount()
        
        // 在广告显示时设置正确的 presentingViewController
        bannerView.presentingViewController = self
    }

    func bannerView(_ bannerView: ATBannerView, didClickWithPlacementID placementID: String, extra: [AnyHashable: Any]!) {
        print("Banner Ad clicked for \(placementID)")
        
    }

    func bannerView(_ bannerView: ATBannerView, didAutoRefreshWithPlacement placementID: String, extra: [AnyHashable: Any]!) {
        print("Banner auto-refresh for \(placementID)")
    }

    func bannerView(_ bannerView: ATBannerView, failedToAutoRefreshWithPlacementID placementID: String, error: Error) {
        print("Banner auto-refresh failed for \(placementID): \(error.localizedDescription)")
    }
    
    // 添加 Banner 广告深度链接/跳转回调
    func bannerView(_ bannerView: ATBannerView, didDeepLinkOrJumpForPlacementID placementID: String, extra: [AnyHashable: Any]!, result success: Bool) {
    }
    
    private func removeNativeAd() {
        // 停止原生广告刷新计时器
        stopNativeAdRefreshTimer()
        
        // 清理自渲染视图
        selfRenderView?.teardown()
        selfRenderView?.removeFromSuperview()
        selfRenderView = nil
        
        // 清理原生广告视图
        nativeAdView?.removeFromSuperview()
        nativeAdView?.destroyNative()
        nativeAdView = nil
        
        // 清理offer
        nativeAdOffer = nil
    }

    
    // 加载插屏广告
    private func loadInterstitialAd() {
        // 加载插屏广告（支持半屏尺寸：如快手；可能会影响展示效果）
        let screenWidth = UIScreen.main.bounds.width
        let size = CGSize(width: screenWidth - 30.0, height: 300.0)
        let extra: [AnyHashable: Any] = [
            kATAdLoadingExtraMediaExtraKey: "ProfileView",
            // 设置半屏插屏广告大小
            kATInterstitialExtraAdSizeKey: NSValue(cgSize: size)
        ]
        ATAdManager.shared().loadAD(withPlacementID: interstitialPlacementID, extra: extra, delegate: self)
    }
    
    // 展示插屏广告
    private func showInterstitialAd() {
        // 检查广告是否准备好
        guard ATAdManager.shared().interstitialReady(forPlacementID: interstitialPlacementID) else {
            print("广告尚未加载完成")
            return
        }

        // 展示广告
        ATAdManager.shared().showInterstitial(withPlacementID: interstitialPlacementID, in: self, delegate: self)
    }
        
    func interstitialDidShow(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
        print("插屏广告展示完成：\(placementID) \(extra)")
        // 展示广告时间戳
        adStartTimes[placementID] = nowMillis()
        incrementTodayAdCount()

    }
    
    func interstitialDidClick(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
        print("插屏广告点击：\(placementID)")
    }
    
    func interstitialDidClose(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
        print("插屏广告关闭：\(placementID) \(extra)")
    }

    
    @objc private func handleIconTap() {
        showSettingsPopup()
    }

    @objc private func handleLabelTap() {
        showSettingsPopup()
    }
    
    private func showSettingsPopup() {
        let settingsPopup = OverlayPanelView()
        settingsPopup.translatesAutoresizingMaskIntoConstraints = false
        settingsPopup.delegate = self   // ✅ 设置代理
        view.addSubview(settingsPopup)
        
        // 添加约束使弹窗覆盖整个屏幕
        NSLayoutConstraint.activate([
            settingsPopup.topAnchor.constraint(equalTo: view.topAnchor),
            settingsPopup.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            settingsPopup.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            settingsPopup.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
    // MARK: - UI Setup
    private func setupUI() {

        // Set Gradient Background
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = view.bounds
        gradientLayer.colors = [UIColor(hexString: "#FF6324")!.cgColor, UIColor(hexString: "#FF9946")!.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0.5, y: 0) // top center
        gradientLayer.endPoint = CGPoint(x: 0.5, y: 1) // bottom center
        view.layer.insertSublayer(gradientLayer, at: 0)

        view.addSubview(bannerContainer)
        view.addSubview(userInfoBGView)
        view.addSubview(iconImageView)
        view.addSubview(userInfoLabel)
        view.addSubview(musiceView)
        
        // New UI components for the right side
        view.addSubview(gainPowerBtn)
        view.addSubview(todayCountLabel)
        view.addSubview(historyCountLabel)

        view.addSubview(boardBGView)
        view.addSubview(verdictBGView)
        view.addSubview(verdictLabel)
        view.addSubview(promptLabel)
        view.addSubview(levelBadge)
        view.addSubview(staminaBadge)
        
        view.addSubview(choiceBtnA)
        view.addSubview(choiceBtnB)
        view.addSubview(markA)
        view.addSubview(markB)
        view.addSubview(nativeAdContainer) // Add native ad container

    }
    
    private func setupConstraints() {
        
        // Set up constraints for banner container
        bannerHeightConstraint = bannerContainer.heightAnchor.constraint(equalToConstant: 0)
        bannerHeightConstraint?.isActive = true

        NSLayoutConstraint.activate([
            // Banner container
            bannerContainer.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,constant: 0),
            bannerContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            bannerContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            
            userInfoBGView.topAnchor.constraint(equalTo: bannerContainer.bottomAnchor,constant: 24),
            userInfoBGView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 24),
            userInfoBGView.widthAnchor.constraint(equalToConstant: 100),
            userInfoBGView.heightAnchor.constraint(equalToConstant: 100),

            iconImageView.centerXAnchor.constraint(equalTo: userInfoBGView.centerXAnchor),
            iconImageView.topAnchor.constraint(equalTo: userInfoBGView.topAnchor,constant: 15),
            iconImageView.widthAnchor.constraint(equalToConstant: 58),
            iconImageView.heightAnchor.constraint(equalToConstant: 58),

            // UID Label
            userInfoLabel.bottomAnchor.constraint(equalTo: userInfoBGView.bottomAnchor, constant: -5),
            userInfoLabel.centerXAnchor.constraint(equalTo: userInfoBGView.centerXAnchor),
            
            musiceView.leadingAnchor.constraint(equalTo: userInfoBGView.trailingAnchor, constant: 14),
            musiceView.centerYAnchor.constraint(equalTo: userInfoLabel.centerYAnchor),

            
            verdictBGView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            verdictBGView.topAnchor.constraint(equalTo: musiceView.bottomAnchor, constant: 14),
            verdictBGView.widthAnchor.constraint(equalToConstant: 290),
            verdictBGView.heightAnchor.constraint(equalToConstant: 72),

            verdictLabel.centerXAnchor.constraint(equalTo: verdictBGView.centerXAnchor),
            verdictLabel.topAnchor.constraint(equalTo: verdictBGView.topAnchor, constant: 11),

            boardBGView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            boardBGView.topAnchor.constraint(equalTo: musiceView.bottomAnchor, constant: 46),
            boardBGView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            boardBGView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),

            // Question Label
            promptLabel.centerYAnchor.constraint(equalTo: boardBGView.centerYAnchor),
            promptLabel.centerXAnchor.constraint(equalTo: boardBGView.centerXAnchor),
            promptLabel.leadingAnchor.constraint(equalTo: boardBGView.leadingAnchor,constant: 20),
            promptLabel.trailingAnchor.constraint(equalTo: boardBGView.trailingAnchor,constant: -20),

            levelBadge.bottomAnchor.constraint(equalTo: boardBGView.bottomAnchor, constant: 14),
            levelBadge.centerXAnchor.constraint(equalTo: boardBGView.centerXAnchor, constant: -67.5),
            levelBadge.heightAnchor.constraint(equalToConstant: 41),
            levelBadge.widthAnchor.constraint(equalToConstant: 111),

            staminaBadge.bottomAnchor.constraint(equalTo: boardBGView.bottomAnchor, constant: 14),
            staminaBadge.centerXAnchor.constraint(equalTo: boardBGView.centerXAnchor, constant: 67.5),
            staminaBadge.heightAnchor.constraint(equalToConstant: 41),
            staminaBadge.widthAnchor.constraint(equalToConstant: 111),

            
            // Option Button 1
            choiceBtnA.topAnchor.constraint(equalTo: boardBGView.bottomAnchor, constant: 45),
            choiceBtnA.centerXAnchor.constraint(equalTo: boardBGView.centerXAnchor),
            choiceBtnA.heightAnchor.constraint(equalToConstant: 47),
            choiceBtnA.widthAnchor.constraint(equalToConstant: 211),

            // Option Button 2
            choiceBtnB.topAnchor.constraint(equalTo: choiceBtnA.bottomAnchor, constant: 20),
            choiceBtnB.centerXAnchor.constraint(equalTo: boardBGView.centerXAnchor),
            choiceBtnB.heightAnchor.constraint(equalToConstant: 47),
            choiceBtnB.widthAnchor.constraint(equalToConstant: 211),
            
            markA.leadingAnchor.constraint(equalTo: choiceBtnA.trailingAnchor, constant: 4),
            markA.centerYAnchor.constraint(equalTo: choiceBtnA.centerYAnchor),
            markA.heightAnchor.constraint(equalToConstant: 40),
            markA.widthAnchor.constraint(equalToConstant: 40),

            markB.leadingAnchor.constraint(equalTo: choiceBtnB.trailingAnchor, constant: 4),
            markB.centerYAnchor.constraint(equalTo: choiceBtnB.centerYAnchor),
            markB.heightAnchor.constraint(equalToConstant: 40),
            markB.widthAnchor.constraint(equalToConstant: 40),


            nativeAdContainer.topAnchor.constraint(equalTo: choiceBtnB.bottomAnchor, constant: 10),
            nativeAdContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            nativeAdContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            nativeAdContainer.heightAnchor.constraint(equalToConstant: 170),

            // New UI components: Right-side Labels and Button
            
            gainPowerBtn.topAnchor.constraint(equalTo: userInfoBGView.topAnchor,constant: 10),
            gainPowerBtn.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            gainPowerBtn.widthAnchor.constraint(equalToConstant: 110),  // Adjust width to fit content
            gainPowerBtn.heightAnchor.constraint(equalToConstant: 37),
            
            todayCountLabel.topAnchor.constraint(equalTo: gainPowerBtn.bottomAnchor, constant: 10),
            todayCountLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            
            historyCountLabel.topAnchor.constraint(equalTo: todayCountLabel.bottomAnchor, constant: 5),
            historyCountLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)

            
            
        ])
    }
    
    // MARK: - Get Power Button Countdown Logic
    private func startGetPowerCountdownIfNeeded() {
        guard !didStartPowerCountdown else { return }
        didStartPowerCountdown = true
        startGetPowerCountdown()
    }
    
    private func startCooldownIfRewardFlowDone() {
        // Start only when server addPower succeeded AND video has been closed
        guard rewardPowerOK && rewardVideoClosed else { return }
        startGetPowerCountdownIfNeeded()
        // reset flags for next cycle
        rewardPowerOK = false
        rewardVideoClosed = false
        // 注意：不在这里清理pendingToastMessage，让Toast先显示
    }

    private func startGetPowerCountdown() {
        // Disable tapping while counting down
        gainPowerBtn.isEnabled = false

        // Build a gray overlay that blocks touches
        if powerCountdownOverlay == nil {
            let overlay = UIView()
            overlay.translatesAutoresizingMaskIntoConstraints = false
            overlay.backgroundColor = UIColor.black.withAlphaComponent(0.5)
            overlay.isUserInteractionEnabled = true // intercept touches while disabled
            overlay.layer.cornerRadius = 18.5
            overlay.clipsToBounds = true

            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false
            label.font = UIFont.boldSystemFont(ofSize: 14)
            label.textColor = .white
            label.textAlignment = .center

            overlay.addSubview(label)

            NSLayoutConstraint.activate([
                label.centerXAnchor.constraint(equalTo: overlay.centerXAnchor),
                label.centerYAnchor.constraint(equalTo: overlay.centerYAnchor)
            ])

            gainPowerBtn.addSubview(overlay)
            NSLayoutConstraint.activate([
                overlay.leadingAnchor.constraint(equalTo: gainPowerBtn.leadingAnchor),
                overlay.trailingAnchor.constraint(equalTo: gainPowerBtn.trailingAnchor),
                overlay.topAnchor.constraint(equalTo: gainPowerBtn.topAnchor),
                overlay.bottomAnchor.constraint(equalTo: gainPowerBtn.bottomAnchor)
            ])

            powerCountdownOverlay = overlay
            powerCountdownLabel = label
        }

        powerRemainingSeconds = staminaWaitSec
        powerCountdownLabel?.text = "\(powerRemainingSeconds)s"

        powerCountdownTimer?.invalidate()
        powerCountdownTimer = Timer.scheduledTimer(timeInterval: 1.0,
                                                   target: self,
                                                   selector: #selector(handlePowerCountdownTick),
                                                   userInfo: nil,
                                                   repeats: true)
        if let timer = powerCountdownTimer {
            RunLoop.main.add(timer, forMode: .common)
        }
    }

    @objc private func handlePowerCountdownTick() {
        powerRemainingSeconds -= 1
        if powerRemainingSeconds > 0 {
            powerCountdownLabel?.text = "\(powerRemainingSeconds)s"
        } else {
            powerCountdownTimer?.invalidate()
            powerCountdownTimer = nil

            // Re-enable the button and remove overlay
            gainPowerBtn.isEnabled = true
            powerCountdownOverlay?.removeFromSuperview()
            powerCountdownOverlay = nil
            powerCountdownLabel = nil
        }
    }

    deinit {
        interstitialTimer?.invalidate()
        interstitialTimer = nil
        nativeTimer?.invalidate()
        nativeTimer = nil
        powerCountdownTimer?.invalidate()
    }

    // Keep a reference to the active Power popup (weak to avoid retain cycle)
    private weak var activePowerPopup: EnergyPanelView?

    // System loading overlay
    private var loadingOverlay: UIView?
    private var loadingSpinner: UIActivityIndicatorView?

    // MARK: - Button Actions
    @objc private func optionButtonTapped(_ sender: UIButton) {
        if sender == choiceBtnA || sender == choiceBtnB {
            handleAnswerSelection(selectedButton: sender)
        }
    }
    
    @objc private func handleGetPowerButtonTap() {
        //若已有体力，则提示并返回
        let currentPower = UserDefaults.standard.object(forKey: "power") as? Int ?? 0
        if canStackPower == 1 && currentPower > 0 {
            showToast(message: "已有体力请先答题")
            return
        }

        let powerPopup = EnergyPanelView()
        powerPopup.translatesAutoresizingMaskIntoConstraints = false
        powerPopup.delegate = self

        // 用最新登录信息配置；若没有，则从 UserDefaults 兜底
        if let info = accountInfo {
            powerPopup.configure(with: info)
            self.activePowerPopup = powerPopup
        } else {
            let nick = UserDefaults.standard.string(forKey: "nickname") ?? "XXXX"
            let role = UserDefaults.standard.string(forKey: "roleID") ?? "XXXX"
            let reg  = UserDefaults.standard.string(forKey: "registryTimeStr") ?? "--"
            let today = UserDefaults.standard.object(forKey: "todayAnswerCount") as? Int ?? 0
            let history = UserDefaults.standard.object(forKey: "historyAnswerCount") as? Int ?? 0
            let head = UserDefaults.standard.string(forKey: "headImgURL")
            let lastLogin = UserDefaults.standard.string(forKey: "lastLoginTimeStr") ?? "--"
            let info = LoginUserInfo(
                nickName: nick,
                userId: role,
                registryTimeStr: reg,
                todayAnswerCount: today,
                historyAnswerCount: history,
                headImgURL: head,
                lastLoginTimeStr: lastLogin,
                answerLogs: UserDefaults.standard.stringArray(forKey: "answerLogs") ?? []
            )
            powerPopup.configure(with: info)
            self.activePowerPopup = powerPopup
        }

        view.addSubview(powerPopup)
        NSLayoutConstraint.activate([
            powerPopup.topAnchor.constraint(equalTo: view.topAnchor),
            powerPopup.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            powerPopup.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            powerPopup.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
    func energyPanelDidTapAcquire(_ panel: EnergyPanelView) {
        // 先把弹窗移除（可按需要保留）
        panel.removeFromSuperview()
        self.activePowerPopup = nil
        // 展示激励广告
        showRewardAd()
    }

    // MARK: - PowerPopupViewDelegate (Close)
    func energyPanelDidDismiss(_ panel: EnergyPanelView) {
        // 关闭体力弹窗后刷新广告
        removeBanner()
        loadBannerAd()
        removeNativeAd()
        loadNativeAd()
        self.activePowerPopup = nil
    }
    
    private func showRealNamePopup() {
        let realNamePopup = IdentityGateView()
        realNamePopup.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(realNamePopup)

        NSLayoutConstraint.activate([
            realNamePopup.topAnchor.constraint(equalTo: view.topAnchor),
            realNamePopup.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            realNamePopup.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            realNamePopup.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    // MARK: - System Loading Helpers
    private func showSystemLoading() {
        guard loadingOverlay == nil else { return }
        let overlay = UIView()
        overlay.translatesAutoresizingMaskIntoConstraints = false
        overlay.backgroundColor = UIColor.black.withAlphaComponent(0.35)
        overlay.isUserInteractionEnabled = true  // block touches

        let spinner = UIActivityIndicatorView(style: .large)
        spinner.translatesAutoresizingMaskIntoConstraints = false
        spinner.startAnimating()
        spinner.hidesWhenStopped = true

        overlay.addSubview(spinner)
        view.addSubview(overlay)
        NSLayoutConstraint.activate([
            overlay.topAnchor.constraint(equalTo: view.topAnchor),
            overlay.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            overlay.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            overlay.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            spinner.centerXAnchor.constraint(equalTo: overlay.centerXAnchor),
            spinner.centerYAnchor.constraint(equalTo: overlay.centerYAnchor)
        ])

        loadingOverlay = overlay
        loadingSpinner = spinner
    }

    private func hideSystemLoading() {
        loadingSpinner?.stopAnimating()
        loadingOverlay?.removeFromSuperview()
        loadingSpinner = nil
        loadingOverlay = nil
    }

    // MARK: - Default Config
    private func fetchDefaultConfig(channel:Channel) {
        guard let url = URL(string: apiBaseURL + getDefaultConfig) else {
            print("[Config] Invalid getDefaultConfig URL")
            return
        }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        let body: [String: Any] = ["ditchId": channel.id]
        request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])
        if let body = request.httpBody, let bodyString = String(data: body, encoding: .utf8) {
            print("[Config] Request URL: \(url)")
            print("[Config] Request Headers: \(request.allHTTPHeaderFields ?? [:])")
            print("[Config] Request Body: \(bodyString)")
        }
        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            if let http = response as? HTTPURLResponse {
                print("[Config] Response Code: \(http.statusCode)")
                print("[Config] Response Headers: \(http.allHeaderFields)")
            }
            if let data = data, let s = String(data: data, encoding: .utf8) {
                print("[Config] Response Body: \(s)")
            }
            if let error = error {
                print("[Config] fetch error: \(error)")
                // 接口失败时，使用默认配置加载广告
                DispatchQueue.main.async { [weak self] in
                    self?.reloadAllAdsWithNewConfig()
                    self?.startInterstitialScheduleIfNeeded()
                }
                return
            }
            guard let data = data else {
                DispatchQueue.main.async { [weak self] in
                    self?.reloadAllAdsWithNewConfig()
                    self?.startInterstitialScheduleIfNeeded()
                }
                return
            }
            // Parse JSON: { code, message, data: { powerWaitTime, interstitialIntervalTime, takuBannerPid, takuInterstitialPid, takuNativePid, takuRewardPid, isAllowNativeAutoRefresh } }
            var newPowerWait: Int?
            var newInterstitial: Int?
            var newNativeInterval: Int?
            var newCanAccumulation: Int?
            var newBannerPid: String?
            var newInterstitialPid: String?
            var newNativePid: String?
            var newRewardPid: String?
            var newIsAllowNativeAutoRefresh: Bool?
            if let obj = try? JSONSerialization.jsonObject(with: data, options: []),
               let dict = obj as? [String: Any],
               let dataDict = dict["data"] as? [String: Any] {
                if let p = dataDict["powerWaitTime"] as? Int { newPowerWait = p }
                if let pStr = dataDict["powerWaitTime"] as? String, let p = Int(pStr) { newPowerWait = p }
                if let i = dataDict["interstitialIntervalTime"] as? Int { newInterstitial = i }
                if let iStr = dataDict["interstitialIntervalTime"] as? String, let i = Int(iStr) { newInterstitial = i }
                if let n = dataDict["flowIntervalTime"] as? Int { newNativeInterval = n }
                if let nStr = dataDict["flowIntervalTime"] as? String, let n = Int(nStr) { newNativeInterval = n }
                if let c = dataDict["canAccumulation"] as? Int { newCanAccumulation = c }
                if let cStr = dataDict["canAccumulation"] as? String, let c = Int(cStr) { newCanAccumulation = c }
                if let cBool = dataDict["canAccumulation"] as? Bool { newCanAccumulation = cBool ? 1 : 0 }
                
                // 获取广告placement ID
                if let bannerPid = dataDict["takuBannerPid"] as? String, !bannerPid.isEmpty { newBannerPid = bannerPid }
                if let interstitialPid = dataDict["takuInterstitialPid"] as? String, !interstitialPid.isEmpty { newInterstitialPid = interstitialPid }
                if let nativePid = dataDict["takuNativePid"] as? String, !nativePid.isEmpty { newNativePid = nativePid }
                if let rewardPid = dataDict["takuRewardPid"] as? String, !rewardPid.isEmpty { newRewardPid = rewardPid }
                
                // 获取原生广告自动刷新开关
                if let allowRefresh = dataDict["canAllowAutoRefresh"] as? Bool { newIsAllowNativeAutoRefresh = allowRefresh }
                if let allowRefreshStr = dataDict["canAllowAutoRefresh"] as? String {
                    if allowRefreshStr.lowercased() == "true" { newIsAllowNativeAutoRefresh = true }
                    else if allowRefreshStr.lowercased() == "false" { newIsAllowNativeAutoRefresh = false }
                }
                if let allowRefreshInt = dataDict["canAllowAutoRefresh"] as? Int { newIsAllowNativeAutoRefresh = allowRefreshInt != 0 }
            }
            DispatchQueue.main.async { [weak self] in
                if let p = newPowerWait, p > 0 {
                    self?.staminaWaitSec = p
                    print("[Config] Applied powerWaitTime = \(p)s")
                }
                self?.startGetPowerCountdownIfNeeded()

                if let i = newInterstitial, i > 0 {
                    self?.interIntervalSec = i
                    print("[Config] Applied interstitialIntervalTime = \(i)s")
                }
                self?.startInterstitialScheduleIfNeeded()
                
                if let n = newNativeInterval, n > 0 {
                    self?.nativeIntervalSec = n
                    print("[Config] Applied flowIntervalTime = \(n)s")
                }

                
                if let c = newCanAccumulation {
                    self?.canStackPower = c
                    print("[Config] Applied canAccumulation = \(c)")
                }
                
                // 更新广告placement ID
                if let bannerPid = newBannerPid {
                    self?.bannerPlacementID = bannerPid
                    print("[Config] Applied takuBannerPid = \(bannerPid)")
                }
                if let interstitialPid = newInterstitialPid {
                    self?.interstitialPlacementID = interstitialPid
                    print("[Config] Applied takuInterstitialPid = \(interstitialPid)")
                }
                if let nativePid = newNativePid {
                    self?.nativeRenderPlacementID = nativePid
                    print("[Config] Applied takuNativePid = \(nativePid)")
                }
                if let rewardPid = newRewardPid {
                    self?.rewardPlacementID = rewardPid
                    print("[Config] Applied takuRewardPid = \(rewardPid)")
                }
                
                // 更新原生广告自动刷新开关
                if let allowRefresh = newIsAllowNativeAutoRefresh {
                    self?.isAllowNativeAutoRefresh = allowRefresh
                    print("[Config] Applied canAllowAutoRefresh = \(allowRefresh)")
                }
                
                // 配置更新后，重新加载所有广告
                self?.reloadAllAdsWithNewConfig()
            }
        }.resume()
    }

    private func startInterstitialScheduleIfNeeded() {
        guard interstitialTimer == nil else { return }
        let interval = max(5, TimeInterval(interIntervalSec)) // safety floor
        interstitialTimer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(handleInterstitialTick), userInfo: nil, repeats: true)
        if let t = interstitialTimer { RunLoop.main.add(t, forMode: .common) }
        print("[Config] Interstitial timer started with interval = \(interval)s")
    }

    @objc private func handleInterstitialTick() {
        // Attempt to show an interstitial; if not ready, trigger a load
        if ATAdManager.shared().interstitialReady(forPlacementID: interstitialPlacementID) {
            showInterstitialAd()
        } else {
            loadInterstitialAd()
        }
    }
    
    // MARK: - Native Ad Auto Refresh
    private func startNativeAdRefreshTimer() {
        guard nativeTimer == nil else { return }
        let interval = max(5, TimeInterval(nativeIntervalSec)) // safety floor
        nativeTimer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(handleNativeAdTick), userInfo: nil, repeats: true)
        if let t = nativeTimer { RunLoop.main.add(t, forMode: .common) }
        print("[Config] Native ad timer started with interval = \(interval)s")
    }
    
    private func stopNativeAdRefreshTimer() {
        nativeTimer?.invalidate()
        nativeTimer = nil
        print("[Config] Native ad timer stopped")
    }
    
    @objc private func handleNativeAdTick() {
        // 刷新原生广告
        removeNativeAd()
        loadNativeAd()
        print("[Config] Native ad refreshed")
    }
    
    // MARK: - Ad Reload with New Config
    private func reloadAllAdsWithNewConfig() {
        print("[Config] Reloading all ads with new placement IDs")
        
        // 重新加载所有广告
        loadBannerAd()
        loadInterstitialAd()
        loadNativeAd()
        loadRewardAd()
    }
    
    // MARK: - Application Lifecycle
    @objc private func appDidEnterForeground() {
        print("[App] App did enter foreground, refreshing native ad")
        // 应用从后台进入前台时刷新原生广告
        refreshNativeAdOnAppBecomeActive()
    }
    
    private func refreshNativeAdOnAppBecomeActive() {
        
        // 移除当前原生广告
        removeNativeAd()
        
        // 重新加载原生广告
        loadNativeAd()
        
        print("[App] Native ad refreshed on app become active")
    }

    // MARK: - Toast Helper
    
    /// 延迟显示Toast消息，等待激励视频关闭后显示
    private func showToastAfterVideoClosed(message: String) {
        print("[Toast] Setting pending message: \(message), videoClosed: \(rewardVideoClosed)")
        pendingToastText = message
        // 如果视频已经关闭，立即显示
        if rewardVideoClosed {
            print("[Toast] Video already closed, showing immediately")
            showPendingToast()
        } else {
            print("[Toast] Video not closed yet, waiting for close event")
        }
    }
    
    /// 显示待显示的Toast消息
    private func showPendingToast() {
        guard let message = pendingToastText else {
            print("[Toast] No pending message to show")
            return 
        }
        print("[Toast] Showing pending message: \(message)")
        pendingToastText = nil
        showToast(message: message)
    }
    
    private func showToast(message: String) {
        // Avoid stacking multiple toasts
        let tag = 9527
        if let existing = view.viewWithTag(tag) {
            existing.removeFromSuperview()
        }
        let toastLabel = UILabel()
        toastLabel.text = message
        toastLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
        toastLabel.textColor = .white
        toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.8)
        toastLabel.textAlignment = .center
        toastLabel.alpha = 0.0
        toastLabel.layer.cornerRadius = 16
        toastLabel.clipsToBounds = true
        toastLabel.numberOfLines = 0
        toastLabel.tag = tag
        toastLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(toastLabel)
        // Padding
        let horizontalPadding: CGFloat = 24
        let verticalPadding: CGFloat = 12
        let maxWidth = view.frame.width - 40
        let size = toastLabel.sizeThatFits(CGSize(width: maxWidth - horizontalPadding * 2, height: CGFloat.greatestFiniteMagnitude))
        let width = min(size.width + horizontalPadding * 2, maxWidth)
        let height = size.height + verticalPadding * 2
        // Center horizontally, bottom at ~20% above bottom
        NSLayoutConstraint.activate([
            toastLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            toastLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            toastLabel.widthAnchor.constraint(equalToConstant: width),
            toastLabel.heightAnchor.constraint(equalToConstant: height)
        ])
        UIView.animate(withDuration: 0.25, animations: {
            toastLabel.alpha = 1.0
        }) { _ in
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                UIView.animate(withDuration: 0.25, animations: {
                    toastLabel.alpha = 0.0
                }) { _ in
                    toastLabel.removeFromSuperview()
                }
            }
        }
    }
    
    // MARK: - Image Loading (Icon)
    private static let imageCache = NSCache<NSURL, UIImage>()
    private func setIconImage(with urlString: String?) {
        guard let s = urlString, !s.isEmpty, let url = URL(string: s) else {
            self.iconImageView.image = UIImage(named: "catlogos")
            return
        }
        if let cached = QuizStageController.imageCache.object(forKey: url as NSURL) {
            self.iconImageView.image = cached
            return
        }
        URLSession.shared.dataTask(with: url) { data, _, _ in
            guard let data = data, let img = UIImage(data: data) else { return }
            QuizStageController.imageCache.setObject(img, forKey: url as NSURL)
            DispatchQueue.main.async { [weak self] in
                self?.iconImageView.image = img
            }
        }.resume()
    }


    // MARK: - Question List
    private func fetchQuestionList() {
        guard var components = URLComponents(string: apiBaseURL + getQuestionListPath) else {
            print("[Question] Invalid question list URL")
            return
        }
        // If backend needs appId, pass it; harmless if ignored
        components.queryItems = [ URLQueryItem(name: "appId", value: kTakuAppID) ]
        guard let url = components.url else { return }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        print("[Question] Request URL: \(url)")

        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            if let http = response as? HTTPURLResponse {
                print("[Question] Response Code: \(http.statusCode)")
                print("[Question] Response Headers: \(http.allHeaderFields)")
            }
            if let data = data, let s = String(data: data, encoding: .utf8) {
                print("[Question] Response Body: \(s)")
            }
            if let error = error {
                print("[Question] fetch error: \(error)")
                return
            }
            guard let data = data else { return }
            let parsed = self?.parseQuestionList(from: data) ?? []
            DispatchQueue.main.async { [weak self] in
                guard let self = self else { return }
                self.questions = parsed

                // 根据登录返回的 lastQuestionId（或本地缓存）定位：从其"下一关"开始
                let lastQid = self.lastQuestionIdFromLogin ?? UserDefaults.standard.string(forKey: "lastQuestionId")
                if let lq = lastQid,
                   !lq.isEmpty,
                   let idx = self.questions.firstIndex(where: { $0.id == lq }) {
                    let nextIndex = idx + 1
                    self.currentQuestionIndex = (nextIndex < self.questions.count) ? nextIndex : 0
                } else {
                    // 为空或没匹配上：从第一关开始
                    self.currentQuestionIndex = 0
                }

                self.renderCurrentQuestion()
            }
        }.resume()
    }

    private func parseQuestionList(from data: Data) -> [QAQuestion] {
        var result: [QAQuestion] = []
        guard let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
              let arr = obj["data"] as? [[String: Any]] else { return result }
        for q in arr {
            let qid = (q["questionId"] as? String) ?? ""
            let content = (q["questionContent"] as? String) ?? ""
            let correct = (q["correctItem"] as? String) ?? ""
            var items: [QAItem] = []
            if let itemList = q["itemList"] as? [[String: Any]] {
                for it in itemList {
                    let iid = (it["itemId"] as? String) ?? ""
                    let ic = (it["itemContent"] as? String) ?? ""
                    items.append(QAItem(id: iid, content: ic))
                }
            }
            if !qid.isEmpty && !content.isEmpty && !items.isEmpty {
                result.append(QAQuestion(id: qid, content: content, correctItemId: correct, items: items))
            }
        }
        return result
    }

    private func renderCurrentQuestion() {
        guard currentQuestionIndex >= 0, currentQuestionIndex < questions.count else { return }
        let q = questions[currentQuestionIndex]
        promptLabel.text = q.content

        // Reset indicators
        markA.isHidden = true
        markB.isHidden = true
        verdictLabel.text = "请选择正确答案"

        // Fill two options (use前两个)
        if q.items.indices.contains(0) {
            choiceBtnA.setTitle(q.items[0].content, for: .normal)
            option1ItemId = q.items[0].id
        } else {
            choiceBtnA.setTitle("选项A", for: .normal)
            option1ItemId = nil
        }
        if q.items.indices.contains(1) {
            choiceBtnB.setTitle(q.items[1].content, for: .normal)
            option2ItemId = q.items[1].id
        } else {
            choiceBtnB.setTitle("选项B", for: .normal)
            option2ItemId = nil
        }

        // Reset option button backgrounds to default
        choiceBtnA.setBackgroundImage(UIImage(named: "res_o67j6d3t"), for: .normal)
        choiceBtnB.setBackgroundImage(UIImage(named: "res_o67j6d3t"), for: .normal)

        // Ensure disabled/gray state persists across questions
        let currentPower = UserDefaults.standard.object(forKey: "power") as? Int ?? 0
        applyPowerToOptionButtons(currentPower)

        // 重置答题状态，允许答题
        isAnswering = false
        
        // 更新关卡显示
        levelBadge.setTitle("关卡:\(currentQuestionIndex + 1)", for: .normal)
        // 题目开始计时点
        questionStartAt = Date()
    }

    private func handleAnswerSelection(selectedButton: UIButton) {
        // 防止重复答题
        guard !isAnswering else {
            print("[答题] 正在处理中，忽略重复点击")
            return
        }
        
        guard currentQuestionIndex < questions.count else { return }
        let q = questions[currentQuestionIndex]

        // 设置答题状态，禁用按钮
        isAnswering = true
        choiceBtnA.isEnabled = false
        choiceBtnB.isEnabled = false

        let selectedId: String?
        let isFirst = (selectedButton == choiceBtnA)
        if isFirst { selectedId = option1ItemId } else { selectedId = option2ItemId }

        // Always显示正确答案标记
        let correctImage = UIImage(named: "res_cwqxdp40")
        let wrongImage = UIImage(named: "res_4h4cvu8h")

        // 先隐藏
        markA.isHidden = true
        markB.isHidden = true

        // 正确项标绿勾
        if option1ItemId == q.correctItemId { markA.image = correctImage; markA.isHidden = false }
        if option2ItemId == q.correctItemId { markB.image = correctImage; markB.isHidden = false }

        // 如果选错，则在所选按钮旁显示红叉
        let isCorrect = (selectedId == q.correctItemId)
        if !isCorrect {
            if isFirst { markA.image = wrongImage; markA.isHidden = false }
            else { markB.image = wrongImage; markB.isHidden = false }
            verdictLabel.text = "回答错误！"
        } else {
            verdictLabel.text = "回答正确！"
        }

        // Update selected button background based on correctness
        if isCorrect {
            selectedButton.setBackgroundImage(UIImage(named: "res_rqqxjzk3"), for: .normal)
        } else {
            selectedButton.setBackgroundImage(UIImage(named: "res_zup626a7"), for: .normal)
        }

        // 计算答题耗时并上报
        let durationSec: Int = {
            if let start = questionStartAt { return max(0, Int(Date().timeIntervalSince(start))) }
            return 0
        }()
        if let sid = selectedId {
            answerQuestion(questionId: q.id, itemId: sid, duration: durationSec)
        }

        // 1秒后进入下一题/结束
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
            self?.goToNextQuestion()
        }
    }
    
    // MARK: - Answer Question API
    private func answerQuestion(questionId: String, itemId: String, duration: Int) {
        guard let url = URL(string: apiBaseURL + answerQuestionPath) else {
            print("[Answer] Invalid URL")
            return
        }
        let userId = accountInfo?.userId ?? (UserDefaults.standard.string(forKey: "roleID") ?? "")
        guard !userId.isEmpty else {
            print("[Answer] Missing userId")
            DispatchQueue.main.async { [weak self] in
                self?.showToast(message: "缺少用户ID，无法上报作答")
            }
            return
        }

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        let body: [String: Any] = [
            "duration": duration,
            "itemId": itemId,
            "questionId": questionId,
            "userId": userId
        ]
        request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])

        // Logs
        if let body = request.httpBody, let bodyString = String(data: body, encoding: .utf8) {
            print("[Answer] Request URL: \(url)")
            print("[Answer] Request Headers: \(request.allHTTPHeaderFields ?? [:])")
            print("[Answer] Request Body: \(bodyString)")
        }

        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            if let http = response as? HTTPURLResponse {
                print("[Answer] Response Code: \(http.statusCode)")
                print("[Answer] Response Headers: \(http.allHeaderFields)")
            }
            if let data = data, let s = String(data: data, encoding: .utf8) {
                print("[Answer] Response Body: \(s)")
            }
            if let error = error {
                print("[Answer] error: \(error)")
                DispatchQueue.main.async { [weak self] in
                    self?.showToast(message: "网络异常，作答上报失败")
                }
                return
            }
            guard let data = data,
                  let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
                return
            }
            let code = obj["code"] as? Int ?? 0
            if code != 200 {
                let msg = (obj["message"] as? String) ?? (obj["msg"] as? String) ?? "作答上报失败"
                DispatchQueue.main.async { [weak self] in
                    self?.showToast(message: msg)
                }
                return
            }

            // 请求成功后扣除体力 + 同步今日/历史答题与作答时间记录
            var returnedToday: Int?
            var returnedHistory: Int?
            var returnedLogs: [String]?
            if let dataDict = obj["data"] as? [String: Any] {
                if let v = dataDict["todayAnswerCount"] as? Int { returnedToday = v }
                else if let s = dataDict["todayAnswerCount"] as? String, let i = Int(s) { returnedToday = i }
                if let v = dataDict["historyAnswerCount"] as? Int { returnedHistory = v }
                else if let s = dataDict["historyAnswerCount"] as? String, let i = Int(s) { returnedHistory = i }
                if let arr = dataDict["answerRecordTimeList"] as? [String] { returnedLogs = arr }
                else if let anyArr = dataDict["answerRecordTimeList"] as? [Any] { returnedLogs = anyArr.compactMap { String(describing: $0) } }
                else if let arr = dataDict["answerRecordList"] as? [String] { returnedLogs = arr }
            }

            DispatchQueue.main.async { [weak self] in
                guard let self = self else { return }
                // 1) 扣体力
        let currentPower = UserDefaults.standard.object(forKey: "power") as? Int ?? 0
        let newPower = max(0, currentPower - 1)
        self.staminaBadge.setTitle("体力:\(newPower)", for: .normal)
                self.applyPowerToOptionButtons(newPower)
                UserDefaults.standard.set(newPower, forKey: "power")
                if newPower == 0 { self.showToast(message: "体力不足，请先领取") }

                // 2) 更新右侧统计（若后端返回）
                if let t = returnedToday {
                    self.todayCountLabel.text = "今日答题: \(t)题"
                    UserDefaults.standard.set(t, forKey: "todayAnswerCount")
                }
                if let h = returnedHistory {
                    self.historyCountLabel.text = "历史答题: \(h)题"
                    UserDefaults.standard.set(h, forKey: "historyAnswerCount")
                }
                if let logs = returnedLogs {
                    UserDefaults.standard.set(logs, forKey: "answerLogs")
                }

                // 3) 更新内存里的 loginUserInfo，用于后续弹窗展示
                if let old = self.accountInfo {
                    let newInfo = LoginUserInfo(
                        nickName: old.nickName,
                        userId: old.userId,
                        registryTimeStr: old.registryTimeStr,
                        todayAnswerCount: returnedToday ?? old.todayAnswerCount,
                        historyAnswerCount: returnedHistory ?? old.historyAnswerCount,
                        headImgURL: old.headImgURL,
                        lastLoginTimeStr: old.lastLoginTimeStr,
                        answerLogs: returnedLogs ?? old.answerLogs
                    )
                    self.accountInfo = newInfo
                    // 若弹窗正在显示，实时刷新
                    self.activePowerPopup?.configure(with: newInfo)
                } else if (returnedToday != nil) || (returnedHistory != nil) || (returnedLogs != nil) {
                    // 构造一个兜底的 LoginUserInfo
                    let nick = UserDefaults.standard.string(forKey: "nickname") ?? "XXXX"
                    let role = UserDefaults.standard.string(forKey: "roleID") ?? ""
                    let reg  = UserDefaults.standard.string(forKey: "registryTimeStr") ?? "--"
                    let head = UserDefaults.standard.string(forKey: "headImgURL")
                    let last = UserDefaults.standard.string(forKey: "lastLoginTimeStr") ?? "--"
                    let t = returnedToday ?? (UserDefaults.standard.object(forKey: "todayAnswerCount") as? Int ?? 0)
                    let h = returnedHistory ?? (UserDefaults.standard.object(forKey: "historyAnswerCount") as? Int ?? 0)
                    let logs = returnedLogs ?? (UserDefaults.standard.stringArray(forKey: "answerLogs") ?? [])
                    let info = LoginUserInfo(nickName: nick, userId: role, registryTimeStr: reg, todayAnswerCount: t, historyAnswerCount: h, headImgURL: head, lastLoginTimeStr: last, answerLogs: logs)
                    self.accountInfo = info
                    self.activePowerPopup?.configure(with: info)
                }
            }
        }.resume()
    }


    private func goToNextQuestion() {
        currentQuestionIndex += 1
        if currentQuestionIndex < questions.count {
            renderCurrentQuestion()
        } else {
            // 题目答完：简单重置或提示
            verdictLabel.text = "已答完全部题目"
            // 也可以重置到第一题：
            // currentQuestionIndex = 0; renderCurrentQuestion()
        }
        
        // 重置答题状态，允许下一题答题
        isAnswering = false
    }

    // MARK: - Power → Option Buttons
    private func applyPowerToOptionButtons(_ power: Int) {
        let enabled = power > 0 && !isAnswering // 体力大于0且不在答题中
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
            guard let self = self else { return }
            self.choiceBtnA.isEnabled = enabled
            self.choiceBtnB.isEnabled = enabled
            // 置灰/恢复
            self.choiceBtnA.alpha = enabled ? 1.0 : 0.5
            self.choiceBtnB.alpha = enabled ? 1.0 : 0.5
        }
    }
    
    // MARK: - Add Power after Reward
    private func addPower() {
        guard var comps = URLComponents(string: apiBaseURL + addPowerPath) else {
            print("[Power] Invalid addPower URL")
            return
        }
        // 优先使用登录后返回的 userId（roleId），否则从持久化读取
        let roleId = accountInfo?.userId ?? (UserDefaults.standard.string(forKey: "roleID") ?? "")
        guard !roleId.isEmpty else {
            print("[Power] Missing userId for addPower")
            DispatchQueue.main.async { [weak self] in
                self?.showToastAfterVideoClosed(message: "缺少用户ID，无法增加体力")
            }
            return
        }
        // GET /wx/addPower?userId=xxx
        comps.queryItems = [ URLQueryItem(name: "userId", value: roleId) ]
        guard let url = comps.url else {
            print("[Power] Failed to build addPower URL with query")
            return
        }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"

        // Request logs (GET，无请求体)
        print("[Power] addPower Request URL: \(url)")
        print("[Power] addPower Request Headers: \(request.allHTTPHeaderFields ?? [:])")

        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            // Response logs
            if let http = response as? HTTPURLResponse {
                print("[Power] addPower Response Code: \(http.statusCode)")
                print("[Power] addPower Response Headers: \(http.allHeaderFields)")
            }
            if let data = data, let s = String(data: data, encoding: .utf8) {
                print("[Power] addPower Response Body: \(s)")
            }
            if let error = error {
                print("[Power] addPower error: \(error)")
                DispatchQueue.main.async { [weak self] in
                    self?.showToastAfterVideoClosed(message: "网络异常，体力更新失败")
                }
                return
            }
            guard let data = data,
                  let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }

            let code = obj["code"] as? Int ?? 0
            if code != 200 {
                let msg = (obj["message"] as? String) ?? (obj["msg"] as? String) ?? "体力更新失败"
                DispatchQueue.main.async { [weak self] in
                    self?.showToastAfterVideoClosed(message: msg)
                }
                return
            }



            var newPower = 0
            if let dataDict = obj["data"] as? [String: Any] {
                if let v = dataDict["power"] as? Int { newPower = v }
                if let s = dataDict["power"] as? String, let i = Int(s) { newPower = i }
            }
            DispatchQueue.main.async { [weak self] in
                guard let self = self else { return }
                self.staminaBadge.setTitle("体力:\(newPower)", for: .normal)
                self.applyPowerToOptionButtons(newPower)
                UserDefaults.standard.set(newPower, forKey: "power")
                // 激励成功 + 服务端加体力成功且激励视频关闭后，才启动获取体力按钮的倒计时
                self.rewardPowerOK = true
                self.didStartPowerCountdown = false
                self.startCooldownIfRewardFlowDone()
                
                
                // 延迟显示成功消息，等待激励视频关闭
                self.showToastAfterVideoClosed(message: "体力增加成功！")

            }
        }.resume()
    }

    
    
    // 广告展示开始时间（毫秒），key 为 placementID
    private var adStartTimes: [String: Int64] = [:]

    // 当前时间戳（毫秒）
    private func nowMillis() -> Int64 {
        Int64(Date().timeIntervalSince1970 * 1000)
    }
    
    // 将毫秒时间戳格式化为 "yyyy-MM-dd HH:mm:ss"
    private func formatMillisToString(_ ms: Int64) -> String {
        let date = Date(timeIntervalSince1970: TimeInterval(ms) / 1000.0)
        let df = DateFormatter()
        df.locale = Locale(identifier: "en_US_POSIX")
        df.timeZone = TimeZone.current // 如果后端要 UTC，就改成 TimeZone(secondsFromGMT: 0)
        df.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return df.string(from: date)
    }
    
    // ===== 当天广告观看次数（持久化到 UserDefaults）=====
    private func adCountKeyForToday() -> String {
        let df = DateFormatter()
        df.locale = Locale(identifier: "en_US_POSIX")
        df.dateFormat = "yyyy-MM-dd"          // 本地自然日
        return "adCount_" + df.string(from: Date())
    }

    private func incrementTodayAdCount() {
        let key = adCountKeyForToday()
        let current = UserDefaults.standard.integer(forKey: key)
        UserDefaults.standard.set(current + 1, forKey: key)
    }

    private func todayAdCount() -> Int {
        let key = adCountKeyForToday()
        return UserDefaults.standard.integer(forKey: key)
    }

    // MARK: - 数据模型
        // 将 TopOn/AnyThink 收益回传转换为服务端接口字段并上报
        private func uploadAdRevenue(placementID: String, extra: [AnyHashable: Any]) {
            // 读取鉴权 token
//            guard let userInfo = UserDefaults.standard.dictionary(forKey: "userInfo"),
//                  let token = userInfo["token"] as? String else {
//                print("[AD-Upload] 无 token，跳过上报")
//                return
//            }

            // 计算 begin / finish（毫秒）并格式化为 "yyyy-MM-dd HH:mm:ss"
            let beginMs = adStartTimes[placementID] ?? nowMillis()
            let finishMs = nowMillis()
            print("testaa \(beginMs) \(String(describing: adStartTimes[placementID])) \(finishMs) \(placementID)")
            adStartTimes.removeValue(forKey: placementID) // 避免复用旧开始时间
            let beginTime = formatMillisToString(beginMs)
            let finishTime = formatMillisToString(finishMs)
            
            // 将 AnyHashable-key 的字典转为 String-key，且递归转为 JSON 可序列化类型
            func jsonSafe(_ value: Any) -> Any {
                if value is String || value is NSNumber || value is NSNull { return value }
                if let d = value as? [String: Any] { return d.mapValues { jsonSafe($0) } }
                if let d = value as? [AnyHashable: Any] {
                    var nd: [String: Any] = [:]
                    d.forEach { nd[String(describing: $0.key)] = jsonSafe($0.value) }
                    return nd
                }
                if let arr = value as? [Any] { return arr.map { jsonSafe($0) } }

                if let v = value as? NSValue {
                    // 处理常见 CoreGraphics 结构体；其余转描述串
                    let type = String(cString: v.objCType)
                    #if canImport(CoreGraphics)
                    if type == "{CGSize=dd}" {
                        let s = v.cgSizeValue
                        return ["width": s.width, "height": s.height]
                    } else if type == "{CGPoint=dd}" {
                        let p = v.cgPointValue
                        return ["x": p.x, "y": p.y]
                    } else if type == "{CGRect={CGPoint=dd}{CGSize=dd}}" {
                        let r = v.cgRectValue
                        return ["x": r.origin.x, "y": r.origin.y, "width": r.size.width, "height": r.size.height]
                    }
                    #endif
                    return v.description
                }

                if let date = value as? Date { return ISO8601DateFormatter().string(from: date) }
                if let url = value as? URL { return url.absoluteString }
                return String(describing: value)
            }

            // 注意：这里用 jsonSafe 处理每个值
            var extraDict: [String: Any] = [:]
            extra.forEach { (k, v) in
                extraDict[String(describing: k)] = jsonSafe(v)
            }

            // 便捷取值工具
            func str(_ key: String) -> String {
                if let v = extraDict[key] { return String(describing: v) }
                return ""
            }
            func intVal(_ key: String) -> Int {
                if let v = extraDict[key] as? Int { return v }
                if let s = extraDict[key] as? String, let i = Int(s) { return i }
                if let d = extraDict[key] as? Double { return Int(d) }
                return 0
            }
            func dbl(_ key: String) -> Double {
                if let v = extraDict[key] as? Double { return v }
                if let s = extraDict[key] as? String, let d = Double(s) { return d }
                if let i = extraDict[key] as? Int { return Double(i) }
                return 0
            }

            // —— 字段映射（对齐安卓） ——
            // adSourceId
            let adSourceId = intVal("adsource_id")
            // networkFormId / networkName / networkPlacementId
            let networkFormId = intVal("network_firm_id")
            let networkName = str("network_name")
            let networkPlacementId = str("network_placement_id")

            // placementId（iOS 回传里一般是 adunit_id；若无，用回调给的 placementID）
            let placementIdValue = str("adunit_id").isEmpty ? placementID : str("adunit_id")

            // recordId（有的 key 叫 requestId，也可能是 req_id，兜底）
            let recordId = str("requestId").isEmpty ? str("req_id") : str("requestId")

            // revenue（publisher_revenue）
            let revenue = dbl("publisher_revenue")

            // userId / nickName
            let userId = UserDefaults.standard.string(forKey: "roleID") ?? ""
            let iosId = UIDevice.current.identifierForVendor?.uuidString ?? ""
            let nickName = UserDefaults.standard.string(forKey: "memberName") ?? ""

            // ecpm：优先取 adsource_price；若没有，用 revenue*1000 估算一个（单位同 revenue）
            var ecpmStr = str("adsource_price")
            if ecpmStr.isEmpty {
                let ecpm = revenue * 1000.0
                ecpmStr = String(format: "%.6f", ecpm)
            }

            // adSourceIndex / adSourceType（优先 adsource_bid_type: 0=非竞价, 1=竞价）
            let adSourceIndex = intVal("adsource_index")
            // adSourceType 依据 adunit_format 映射：Native->0, RewardedVideo->1, Banner->2, Interstitial->3
            let adUnitFormatRaw = str("adunit_format")
            let adUnitFormat = adUnitFormatRaw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
            let adSourceType: Int
            switch adUnitFormat {
            case "native":
                adSourceType = 0
            case "rewardedvideo", "rewarded_video", "rewarded":
                adSourceType = 1
            case "banner":
                adSourceType = 2
            case "interstitial":
                adSourceType = 3
            default:
                // 兜底逻辑：保持原有取值（若 SDK 提供 adsource_bid_type 则沿用）
                adSourceType = intVal("adsource_bid_type")
            }

            // resultJson：把 SDK 回传完整塞进去，便于排查
            let resultJsonData = try? JSONSerialization.data(withJSONObject: extraDict, options: [])
            let resultJson = resultJsonData.flatMap { String(data: $0, encoding: .utf8) } ?? "{}"
            
            // appId：iOS 若无可用 App ID，可用 Bundle Identifier 兜底
            let appId = kTakuAppID

            let adCount = todayAdCount()
            
            // 组装请求体（与安卓 map 字段完全一致）
            let body: [String: Any] = [
                "adSourceId": adSourceId,
                "beginTime": beginTime,
                "finishTime": finishTime,
                "networkFormId": networkFormId,
                "networkName": networkName,
                "networkPlacementId": networkPlacementId,
                "nickName": nickName,
                "placementId": placementIdValue,
                "recordId": recordId,
                "revenue": revenue,
                "userId": userId,
                "ecpm": ecpmStr,
                "adSourceIndex": adSourceIndex,
                "adSourceType": adSourceType,
                "resultJson": resultJson,
                "appId": appId,
                "adCount": adCount,
                "iosId": iosId,
                "begintimestamp": beginMs,
                "finishtimestamp": finishMs
            ]
//            print("testggg \(body)")
            // 发起上报
            let urlString = apiBaseURL + adSaveRecordPath
//            print("[AD-Upload] URL \(urlString)")
            guard let url = URL(string: urlString) else {
                print("[AD-Upload] URL 无效: \(urlString)")
                return
            }

            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
//            request.setValue(token, forHTTPHeaderField: "Authorization")
            request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])

            if let body = request.httpBody,
               let bodyString = String(data: body, encoding: .utf8) {
                print("[AD-Upload] Request URL: \(url)")
                print("[AD-Upload] Request Headers: \(request.allHTTPHeaderFields ?? [:])")
                print("[AD-Upload] Request Body: \(bodyString)")
            }

            URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
                if let error = error {
                    print("[AD-Upload] 网络错误: \(error.localizedDescription)")
                    return
                }
                if let http = response as? HTTPURLResponse {
                    print("[AD-Upload] 响应状态码: \(http.statusCode)")
                }

                if let data = data, let s = String(data: data, encoding: .utf8) {
                    print("[AD-Upload] 响应: \(s)")
                    // 解析 JSON，并在 code == 301 时弹 toast 并延迟跳转登录
                    if let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                        let codeVal: Int = {
                            if let i = obj["code"] as? Int { return i }
                            if let str = obj["code"] as? String, let i = Int(str) { return i }
                            return 0
                        }()
                        if codeVal == 301 {
                            let message = (obj["message"] as? String) ?? (obj["msg"] as? String) ?? "软件被手机系统限制！请稍后再试！"
                            DispatchQueue.main.async { [weak self] in
                                self?.showToast(message: message)
                                DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
                                    self?.switchToLoginScreen()
                                }
                            }
                        }
                    }
                }
            }.resume()
        }

    private func switchToLoginScreen() {
//        // 清除用户登录状态
//        UserDefaults.standard.set(false, forKey: "isLoggedIn")
//        UserDefaults.standard.removeObject(forKey: "userInfo")

        // 创建登录界面
        let loginVC = EntryGateController()
//        let navController = UINavigationController(rootViewController: loginVC)
        
        // 获取当前窗口
        guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else {
            return
        }
        
        // 设置根视图控制器为登录界面
        window.rootViewController = loginVC
        
        // 添加切换动画
        UIView.transition(with: window,
                          duration: 0.5,
                          options: .transitionCrossDissolve,
                          animations: nil,
                          completion: nil)
    }

    
    // MARK: - OverlayPanelAgent
    func overlayPanelDidDismiss(_ panel: OverlayPanelView) {
        // 刷新横幅广告
        removeBanner()
        loadBannerAd()

        // 刷新信息流（原生）广告
        removeNativeAd()
        loadNativeAd()
    }
}


// MARK: - UIColor Extension
extension UIColor {
    convenience init?(hexString: String) {
        var hex = hexString.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
        
        if hex.hasPrefix("#") {
            hex.remove(at: hex.startIndex)
        }
        
        var rgbValue: UInt64 = 0
        Scanner(string: hex).scanHexInt64(&rgbValue)
        
        let r = CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0
        let g = CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0
        let b = CGFloat(rgbValue & 0x0000FF) / 255.0
        
        self.init(red: r, green: g, blue: b, alpha: 1.0)
    }
}



