//
//  PhoneCallController.swift

//
//  Created by Leon on 2025/8/19.
//

import Lottie
import UIKit
import Combine
import DateToolsSwift
import SwiftDate

enum PhoneCallUIState: Int {
    case onCallAIDefault = 0
    case calling = 1 // 拨通了，表示STAR调用成功
    case onCallAISaying = 2
    case onCallAIListening = 3 // 即用户在说话
    case onCallAIThinking = 4
    
    var display:String{
        switch self {
        case .onCallAIDefault:
            return "默认状态，未接通"
        case .calling:
            return "接通电话"
        case .onCallAISaying:
            return "AI Saying"
        case .onCallAIListening:
            return "AI Listening"
        case .onCallAIThinking:
            return "AI thinking"
        }
    }
}

class PhoneCallController: CLBaseViewController, PhoneCallViewModelDelegate {

    var bgIv: UIImageView!
    var effectView: UIVisualEffectView!
    
    // Case 1
    var callingAvatar: CLImageView!
    
    // Case 2
    var avatarsContainer: UIView!
    var avatarStackH: UIStackView!
    var avatar1: CLImageView!
    var avatar2: CLImageView!
    var hearIcon: UIImageView!
    var heartLottie: LottieAnimationView!
    var heartLevelLabel: UILabel!
    
    var nameLabel: UILabel!
    var heartLevelTag: RelationshipTag!
    /// 升降级的NoticeView
    var upDownNoticeView : SessionHeartLevelNoticeView!
    
    // Bottom

    var hangUpButton: EPIconDestructiveButton!
    var stateOfCallLabel: CLLabel!
    var interruptButton: StyleButton!
    var threeDotsAnimationView: LottieAnimationView!
    var wavingVoiceView : PhontVoiceWavingView!
    var wordsScrollContainer: LTScrollContainer!
    var spacerView: UIView!
    var wordsOtherSayLabel: LineSpaceLabel!
    
    lazy var testLabel = UILabel()
    lazy var testButton = UIButton()

    // MARK: Flag
    var callStartDate: Date = Date()
    var testStepIndex: Int = 0
    
    // MARK: Data
    var aiId : Int?
    var viewModel = PhoneCallViewModel()
    @Published var viewState: PhoneCallUIState = .onCallAIDefault
    private var cancellables = Set<AnyCancellable>()
    
    var ringTone: SpeechModel?
    
    // MARK: AI说话超时控制
    /// AI说话超时时间（秒），默认1秒
    var aiSpeakingTimeout: TimeInterval = 1.0
    private var aiSpeakingTimer: Timer?
    
    // MARK: 用户说话超时控制
    /// 用户停止说话后切换到思考状态的超时时间（秒），默认2秒
    var userSpeakingTimeout: TimeInterval = 1.0
    private var userSpeakingTimer: Timer?
    /// 标记用户是否刚刚说过话，只有用户说话后停止说话才会切换到thinking状态
    private var userJustSpoke: Bool = false
    
    // MARK: - Control
    lazy var timestampThisTime = Date().timeStamp
    
    /// 回调电话状态，duration：（单位毫秒）
    var callEventAction:((_ type: CallType, _ duration: Int? ) -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        setupViews()
        setupDatas()
        setupEvents()
        playRingtone()
        #if DEBUG
        testUI()
        #endif
    }

    private func setupDatas() {
        
        guard let user = IMAIViewModel.shared.aiIMInfo else{
            return
        }
        aiId = user.aiId
        viewModel.aiId = aiId
        viewModel.delegate = self
        
        refreshViews()
        // Default state
        viewState = .onCallAIDefault

//        #warning("test")
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {[weak self] in
            self?.startCall()
        }
    }
    
    private func refreshViews(){
        guard let user = IMAIViewModel.shared.aiIMInfo else{
            return
        }
        bgIv.loadImage(user.backgroundImg)
        
        // 心动等级
        heartLevelLabel.text = "\(user.aiUserHeartbeatRelation?.heartbeatLevelNum ?? 1)"
        nameLabel.text = user.nickname
        callingAvatar.loadImage(user.headImg)
        avatar1.loadImage(user.headImg)
        avatar2.loadImage(UserCore.shared.user?.headImage)
        
        refreshOnlyRelationTag(user.aiUserHeartbeatRelation?.heartbeatVal)
    }
    
    private func refreshOnlyRelationTag(_ val : CGFloat?){
        guard let user = IMAIViewModel.shared.aiIMInfo else{
            return
        }
        heartLevelTag.bind(level: user.aiUserHeartbeatRelation?.heartbeatLevel, heartBeatVal: val, isShow: user.aiUserHeartbeatRelation?.isShow)
    }
    
    private func startCall(){
        viewModel.loadRtc {[weak self] result in
            guard let `self` = self else {
                return
            }
            
            if result{
                self.viewModel.opt(type: .START, timestamp: self.timestampThisTime) {[weak self] result in
                    Hud.hideIndicator()
                    if result{
                        SpeechManager.shared.stopPlayCurrent()
                        self?.callStartDate = Date()
                        self?.viewState = .calling
                    }else{
                        self?.tapHangUp()
                    }
                }
            }else{
                Hud.hideIndicator()
            }
        }
    }

    
    private func testData(){
        bgIv.image = UIImage(named: "egpic")?.cropImageTop(with: 1 / UIScreen.aspectRatio)

        heartLevelLabel.text = "1"
        nameLabel.text = "ASAF"
        stateOfCallLabel.text = "Click to interrupt"

        let url = UserCore.shared.user?.headImage
        avatar1.loadImage(url)
        avatar2.loadImage(url)

        let addition = "(Show surprise)"
        let words = "Hi, why did you remember to call me? It's not easy \(addition)"
        let aStr = words.withAttributes([.font(.t.tbl), .textColor(.text)])
        let ranges = words.matchStrRange(addition) // words.range(of: addition)
        let att = [NSAttributedString.Key.font: UIFont.t.tbl,
                   NSAttributedString.Key.foregroundColor: UIColor.c.ctsn,
        ]
        for range in ranges {
            aStr.addAttributes(att, range: range)
        }
        wordsOtherSayLabel.attributedText = aStr

        threeDotsAnimationView.play()
    }

    private func setupEvents() {
        navigationView.tapBackButtonAction = {[weak self] in
            self?.tapHangUp()
        }
        
        $viewState.sink {[weak self] state in
            #if DEBUG
            //Hud.hideToast()
            //Hud.toast(str: state.display)
            self?.testLabel.text = state.display
            #endif
            self?.refreshViewsState(state: state)
        }.store(in: &cancellables)
        
        IMAIViewModel.shared.$aiIMInfo.sink {[weak self] info in
            self?.refreshViews()
        }.store(in: &cancellables)
        
        IMAIViewModel.shared.$heartbeatVal.sink {[weak self] val in
            dlog("Only $heartbeatVal \(String(describing: val)) ")
            self?.refreshOnlyRelationTag(val)
        }.store(in: &cancellables)
        
        IMAIViewModel.shared.$heartbeatLevelUpString.sink {[weak self] string in
            guard let notice = string, notice.count > 0 else {return}
            self?.heartLottie.isHidden = false
            self?.heartLottie.play {[weak self] completed in
                self?.heartLottie.isHidden = true
            }
            self?.heartLevelTag.isHidden = true
            self?.upDownNoticeView.showUnlocked(string: notice) {
                self?.heartLevelTag.isHidden = false
            }
        }.store(in: &cancellables)
        
        IMAIViewModel.shared.$heartbeatLevelDownString.sink { [weak self] string in
            guard let notice = string, notice.count > 0 else {return}
            self?.heartLottie.isHidden = false
            self?.heartLottie.play {[weak self] completed in
                self?.heartLottie.isHidden = true
            }
            self?.heartLevelTag.isHidden = true
            self?.upDownNoticeView.showLose(string: notice) {
                self?.heartLevelTag.isHidden = false
            }
        }.store(in: &cancellables)
        
        viewModel.subTitleCallbck = {[weak self] text, userIdString in
            if let userId = Int(userIdString), userId == UserCore.shared.user?.userId{
                // 本人在说话
                self?.viewState = .onCallAIListening
                // 取消AI说话定时器
                self?.clearAISpeakingTimer()
                
                // 重置用户说话定时器
                self?.resetUserSpeakingTimer()
                
                // 标记用户刚刚说过话
                self?.userJustSpoke = true
            }else{
                self?.viewState = .onCallAISaying
                
                // 重置AI说话定时器
                self?.resetAISpeakingTimer()
                
                // 取消用户说话定时器
                self?.clearUserSpeakingTimer()
                
                self?.wordsOtherSayLabel.attributedText = self?.formatAttrubuteString(string: text)
                if let labelHeight = self?.wordsOtherSayLabel.size.height, let containerHeight = self?.wordsScrollContainer.size.height{
                    //dlog("label's height\(labelHeight),  stack' s height\(containerHeight)")
                    if labelHeight < containerHeight{
                        self?.spacerView.snp.updateConstraints { make in
                            make.height.equalTo(containerHeight - labelHeight)
                        }
                    }else{
                        self?.spacerView.snp.updateConstraints { make in
                            make.height.equalTo(0)
                        }
                    }
                }
                // 自动滚动到底部
                self?.scrollToBottom()
            }
        }
        
        viewModel.leaveTheRoomForcedAction = {[weak self] in
            self?.close()
        }
        
    }
    
    func phoneCallLocalUserSaying(saying: Bool){
        print("💬 syaing: \(saying)")
        
        if viewState == .onCallAIListening{
            // #warning("to do， check状态对不对,待测试")
            wavingVoiceView.play()
        }
    }

    // MARK: - Action
    @objc private func tapHangUp() {
        
//        #warning("test")
//        viewModel.leaveRoom()
//        close(dismissFirst: true)
//        return
        
//        #warning("test")
//        //callEventAction?(.CALL_CANCEL, nil)
//        callEventAction?(.CALL_END, 6500)
//        close(dismissFirst: true)
//        return
        
        // 清理AI说话定时器
        clearAISpeakingTimer()
        
        guard viewState.rawValue >= PhoneCallUIState.calling.rawValue else{
            viewModel.leaveRoom()
            callEventAction?(.CALL_CANCEL, nil)
            close(dismissFirst: true)
            return
        }
        
        let now = Date()
        let miniseconds = now.timeStamp - self.callStartDate.timeStamp
        
        Hud.showIndicator()
        viewModel.opt(type: .STOP, timestamp: timestampThisTime, duration: miniseconds) {[weak self] result in
            Hud.hideIndicator()
            guard let self = self else{return}
            if result{
                self.viewModel.leaveRoom()
                
                self.callEventAction?(.CALL_END, miniseconds)
                // 是否退出页面
                self.close(dismissFirst: true)
            }
        }
    }
    
    @objc private func tapInterruptButton(){
        Hud.showIndicator()
        viewModel.opt(type: .INTERRUPT, timestamp: timestampThisTime) { _ in
            Hud.hideIndicator()
        }
    }
    
    // MARK: - Functions
    
    private func playRingtone() {
        guard let path = Bundle.main.path(forResource: "call_ringtone", ofType: "mp3") else { return }
        let model = SpeechManager.shared.modelWithFilePath(path)
        ringTone = model
        
        model.stateChangedBlock = { [weak self] updatedModel in
            guard let self = self else { return }
            self.checkState(model: updatedModel)
        }
        
        SpeechManager.shared.startPlay(with: model)
    }
    
    private func checkState(model: SpeechModel) {
        guard model.loadState == .complete else {
            // SpeechManager.shared.stopPlay(with: model) // ❌
            return
        }
        
        switch model.playState {
        case .default:
            if model.canAutoPlay {
                SpeechManager.shared.startPlay(with: model)
            }
        case .complete, .failed:
            SpeechManager.shared.stopPlay(with: model)
        case .playing:
            break
        }
    }
    
    // MARK: - Helper
    
    private func scrollToBottom() {
        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }
            let bottomOffset = CGPoint(x: 0, y: max(0, self.wordsScrollContainer.scrollView.contentSize.height - self.wordsScrollContainer.scrollView.bounds.height))
            self.wordsScrollContainer.scrollView.setContentOffset(bottomOffset, animated: true)
        }
    }
    
    /// 重置AI说话定时器
    private func resetAISpeakingTimer() {
        // 取消之前的定时器
        aiSpeakingTimer?.invalidate()
        
        // 创建新的定时器
        aiSpeakingTimer = Timer.scheduledTimer(withTimeInterval: aiSpeakingTimeout, repeats: false) { [weak self] _ in
            // 超时后切换到AI不说话状态
            self?.viewState = .onCallAIListening
            
            self?.wavingVoiceView.stop()
        }
    }
    
    /// 清理AI说话定时器
    private func clearAISpeakingTimer() {
        aiSpeakingTimer?.invalidate()
        aiSpeakingTimer = nil
    }
    
    /// 重置用户说话定时器
    private func resetUserSpeakingTimer() {
        // 取消之前的定时器
        userSpeakingTimer?.invalidate()
        
        // 说明还在说话中...
        if wavingVoiceView.isAnimationPlaying == false{
            wavingVoiceView.play()
        }
        
        // 创建新的定时器
        userSpeakingTimer = Timer.scheduledTimer(withTimeInterval: userSpeakingTimeout, repeats: false) { [weak self] _ in
            // 只有用户刚刚说过话，停止说话才切换到思考状态
            if self?.userJustSpoke == true {
                self?.viewState = .onCallAIThinking
                self?.userJustSpoke = false // 重置标志
            }
            
        }
    }
    
    /// 清理用户说话定时器
    private func clearUserSpeakingTimer() {
        userSpeakingTimer?.invalidate()
        userSpeakingTimer = nil
    }
    
    private func showThreeDotAnimation(_ show: Bool){
        if show{
            threeDotsAnimationView.isHidden = false
            threeDotsAnimationView.play()
        }else{
            threeDotsAnimationView.isHidden = true
            threeDotsAnimationView.stop()
        }
    }
    
    private func showWaveWavingAnimation(_ show: Bool){
        if show{
            wavingVoiceView.isHidden = false
            wavingVoiceView.play()
        }else{
            wavingVoiceView.isHidden = true
            wavingVoiceView.stop()
        }
    }
    
    private func refreshViewsState(state: PhoneCallUIState){
        switch state {
        case .onCallAIDefault:
            showThreeDotAnimation(true)
            showWaveWavingAnimation(false)
            stateOfCallLabel.isHidden = false
            callingAvatar.isHidden = false
            avatarsContainer.isHidden = true
            interruptButton.isHidden = true
            threeDotsAnimationView.play()
            stateOfCallLabel.text = "Waiting to be connected"//"..." // 初始的加载过程中
        case .calling:
            showThreeDotAnimation(true)
            showWaveWavingAnimation(false)
            stateOfCallLabel.isHidden = false
            callingAvatar.isHidden = true
            avatarsContainer.isHidden = false
            interruptButton.isHidden = true
            threeDotsAnimationView.play()
            stateOfCallLabel.text = "  "//"Waiting to be connected"
            
        case .onCallAISaying:
            showThreeDotAnimation(false)
            showWaveWavingAnimation(false)
            stateOfCallLabel.isHidden = true
            callingAvatar.isHidden = true
            avatarsContainer.isHidden = false
            interruptButton.isHidden = false
            threeDotsAnimationView.stop()
            break
        case .onCallAIListening:
            showThreeDotAnimation(false)
            showWaveWavingAnimation(true)
            stateOfCallLabel.isHidden = false
            callingAvatar.isHidden = true
            avatarsContainer.isHidden = false
            interruptButton.isHidden = true
            stateOfCallLabel.text = "Listening..."
            break
        case .onCallAIThinking:
            showThreeDotAnimation(true)
            showWaveWavingAnimation(false)
            stateOfCallLabel.isHidden = false
            callingAvatar.isHidden = true
            avatarsContainer.isHidden = false
            interruptButton.isHidden = true
            stateOfCallLabel.text = "Thinking..."
        }
    }
    
    private func formatAttrubuteString(string: String) -> NSMutableAttributedString{
        let content = string
        let aStr = content.withAttributes([.font(.t.tbm), .textColor(.text)])
        let ranges = String.findBracketRanges(in: content)
        let att = [NSAttributedString.Key.font: UIFont.t.tbm,
                   NSAttributedString.Key.foregroundColor: UIColor.c.ctsn,
        ]
        for range in ranges {
            aStr.addAttributes(att, range: range)
        }
        return aStr
    }
    
    // MARK: - UI
    
    private func setupViews() {
        navigationView.setupBackButtonCloseIcon()
        navigationView.bgView.alpha = 0

        bgIv = {
            let v = UIImageView()
            v.contentMode = .scaleAspectFill
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.leading.top.trailing.equalToSuperview()
                make.bottom.equalToSuperview()
            }
            return v
        }()

        effectView = {
            let v = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
            v.alpha = 0.9
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.edges.equalToSuperview()
            }
            return v
        }()
        
        callingAvatar = {
            let v = CLImageView()
            v.cornerRadius = 64
            v.backgroundColor = .c.csbn
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.size.equalTo(CGSize(width: 128, height: 128))
                make.centerX.equalToSuperview()
                make.top.equalTo(navigationView.snp.bottom).offset(48)
            }
            return v
        }()

        avatarsContainer = {
            let v = UIView()
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.leading.trailing.equalToSuperview()
                make.top.equalTo(navigationView.snp.bottom).offset(48)
                make.height.equalTo(128)
            }
            v.isHidden = true
            return v
        }()

        avatarStackH = {
            let v = UIStackView()
            v.spacing = 16
            avatarsContainer.addSubview(v)
            v.snp.makeConstraints { make in
                make.center.equalToSuperview()
            }
            return v
        }()

        avatar1 = {
            let v = CLImageView()
            v.cornerRadius = 64
            v.backgroundColor = .c.csbn
            avatarStackH.addArrangedSubview(v)
            v.snp.makeConstraints { make in
                make.size.equalTo(CGSize(width: 128, height: 128))
            }
            return v
        }()

        avatar2 = {
            let v = CLImageView()
            v.cornerRadius = 64
            v.backgroundColor = .c.csbn
            avatarStackH.addArrangedSubview(v)
            v.snp.makeConstraints { make in
                make.size.equalTo(CGSize(width: 128, height: 128))
            }
            return v
        }()

        hearIcon = {
            let v = UIImageView()
            v.image = UIImage(named: "chat_level_heart")
            avatarsContainer.addSubview(v)
            v.snp.makeConstraints { make in
                make.size.equalTo(CGSize(width: 80, height: 80))
                make.center.equalToSuperview()
            }
            return v
        }()
        
        heartLottie = {
            let animation = LottieAnimation.named("heartbeat_pink")
            let animationView = LottieAnimationView(animation: animation)

            animationView.contentMode = .scaleAspectFit
            animationView.loopMode = .playOnce
            animationView.backgroundBehavior = .pauseAndRestore
            animationView.backgroundColor = .clear
            animationView.size = CGSize(width: 44, height: 44)
            //avatarsContainer.addSubview(animationView)
            avatarsContainer.insertSubview(animationView, belowSubview: hearIcon)
            animationView.snp.makeConstraints { make in
                make.center.equalTo(hearIcon)
                make.size.equalTo(CGSize(width: 240, height: 210))
            }
            animationView.isHidden = true
            return animationView
        }()

        heartLevelLabel = {
            let v = UILabel()
            v.textColor = .text
            v.font = .t.tnml
            avatarsContainer.addSubview(v)
            v.snp.makeConstraints { make in
                make.center.equalTo(hearIcon)
            }
            return v
        }()

        nameLabel = {
            let v = UILabel()
            v.font = .t.ttl
            v.textColor = .text
            v.textAlignment = .center
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.top.equalTo(avatarStackH.snp.bottom).offset(16)
                make.leading.equalToSuperview().offset(24)
                make.trailing.equalToSuperview().offset(-24)
            }
            return v
        }()
        
        heartLevelTag = {
            let v = RelationshipTag(size: .large)
            v.effectView.alpha = 0
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.centerX.equalToSuperview()
                make.top.equalTo(nameLabel.snp.bottom).offset(16)
            }
            return v
        }()
        
        upDownNoticeView = {
            let v = SessionHeartLevelNoticeView()
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.centerX.equalToSuperview()
                make.top.equalTo(nameLabel.snp.bottom).offset(16)
            }
            return v
        }()

        setupBottomView()

        view.bringSubviewToFront(navigationView)
    }

    private func setupBottomView() {
        hangUpButton = {
            let v = EPIconDestructiveButton(radius: .round, iconSize: .large, iconCode: .iconCallHangup)
            v.addTarget(self, action: #selector(tapHangUp), for: .touchUpInside)
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.size.equalTo(CGSizeMake(48, 48))
                make.centerX.equalToSuperview()
                make.bottom.equalToSuperview().offset(-24 - UIWindow.safeAreaBottom - 16)
            }

            return v
        }()

        stateOfCallLabel = {
            let v = CLLabel()
            v.font = .t.tlm
            v.textColor = .desc
            v.textAlignment = .center
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.leading.equalToSuperview().offset(24)
                make.trailing.equalToSuperview().offset(-24)
                make.bottom.equalTo(hangUpButton.snp.top).offset(-24)
            }
            return v
        }()

        threeDotsAnimationView = {
            let animation = LottieAnimation.named("three_dots_msg_loading")
            let animationView = LottieAnimationView(animation: animation)

            animationView.contentMode = .scaleAspectFit
            animationView.loopMode = .loop
            animationView.backgroundBehavior = .pauseAndRestore
            animationView.backgroundColor = .clear
            animationView.size = CGSize(width: 70, height: 40)
            view.addSubview(animationView)
            animationView.snp.makeConstraints { make in
                make.size.equalTo(CGSize(width: 70, height: 40))
                make.centerX.equalToSuperview()
                //make.bottom.equalTo(stateOfCallLabel.snp.top).offset(-16) // -24
                make.centerY.equalTo(stateOfCallLabel.snp.top).offset(-24)
            }
            return animationView
        }()
        
        wavingVoiceView = {
            let v = PhontVoiceWavingView()
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.size.equalTo(CGSize(width: 156, height: 24))
                make.centerX.equalToSuperview()
                //make.bottom.equalTo(stateOfCallLabel.snp.top).offset(-24)
                make.centerY.equalTo(threeDotsAnimationView)
            }
            return v
        }()
        
        interruptButton = {
            let v = StyleButton(type: .custom)
            v.tertiary(size: .small)
            v.setTitle("Interrupt", for: .normal)
            v.addTarget(self, action: #selector(tapInterruptButton), for: .touchUpInside)
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.centerX.equalToSuperview()
                make.bottom.equalTo(hangUpButton.snp.top).offset(-24)
            }
            v.isHidden = true
            return v
        }()
        
        wordsScrollContainer = {
            let v = LTScrollContainer()
            view.addSubview(v)
            v.snp.makeConstraints { make in
                make.leading.trailing.equalToSuperview()
                make.top.equalTo(heartLevelTag.snp.bottom).offset(16)
                //make.bottom.equalTo(stateOfCallLabel.snp.top).offset(-88)
                make.bottom.equalTo(hangUpButton.snp.top).offset(-108)
            }
            return v
        }()
        // 添加一个占位视图，让字幕始终显示在底部
        let spacerView = UIView()
        wordsScrollContainer.stack.addArrangedSubview(spacerView)
        spacerView.snp.makeConstraints { make in
            //make.height.greaterThanOrEqualTo(0)
            make.height.equalTo(0)
        }
        self.spacerView = spacerView
        
        wordsOtherSayLabel = {
            let v = LineSpaceLabel()
            v.textColor = .white
            wordsScrollContainer.stack.addArrangedSubview(v)
            v.snp.makeConstraints { make in
                make.leading.equalToSuperview().offset(24)
                make.trailing.equalToSuperview().offset(-24)
            }
            return v
        }()
        

        
        // 设置 stack 的对齐方式，让内容靠底部显示
        wordsScrollContainer.stack.alignment = .fill
        wordsScrollContainer.stack.distribution = .fill
    }
    
    deinit {
        clearAISpeakingTimer()
        clearUserSpeakingTimer()
    }
    
    // MARK: - Test
    
    private func testUI(){
        view.addSubview(testLabel)
        testLabel.textColor = .white
        testLabel.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(UIWindow.statusBarHeight + 8)
            make.centerX.equalToSuperview()
        }
        
        view.addSubview(testButton)
        testButton.setTitle("Switch view State", for: .normal)
        testButton.backgroundColor = .random
        testButton.snp.makeConstraints { make in
            make.top.equalTo(testLabel.snp.bottom).offset(12)
            make.centerX.equalToSuperview()
        }
        testButton.addTarget(self, action: #selector(testCall), for: .touchUpInside)
        
        let close = UIButton()
        view.addSubview(close)
        close.backgroundColor = .random
        close.setTitle("Close", for: .normal)
        close.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(testButton.snp.bottom).offset(8)
        }
        close.addTap {[weak self] btn in
            self?.close()
        }
    }

    @objc private func testCall(){
//        self.testStepIndex += 1
//        let nextState = PhoneCallUIState(rawValue: self.testStepIndex) ?? .onCallAIDefault
//        self.viewState = nextState
        
        viewState = .calling
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            IMAIViewModel.shared.heartbeatLevelUpString =  "xxxxxxxxx"
        }
        
    }
}
