//
//  LNProfileManager.swift
//  Lanu
//
//  Created by OneeChan on 2025/11/6.
//

import Foundation


protocol LNProfileManagerNotify {
    func onUserInfoChanged(userInfo: LNUserProfileVO)
    func onBindPhoneCaptchaCoolDownChanged(time: Int)
    func onUserVoiceBarInfoChanged()
}
extension LNProfileManagerNotify {
    func onUserInfoChanged(userInfo: LNUserProfileVO) { }
    func onBindPhoneCaptchaCoolDownChanged(time: Int) { }
    func onUserVoiceBarInfoChanged() { }
}

var myUserInfo: LNUserProfileVO {
    LNProfileManager.shared.myUserInfo
}

var myVoiceBarInfo: LNUserVoiceStateVO {
    LNProfileManager.shared.myVoiceState
}

class LNProfileManager {
    static let shared = LNProfileManager()
    static let nameMaxInput = 25
    static let bioMaxInput = 100
    
    fileprivate var myUserInfo: LNUserProfileVO = LNUserProfileVO()
    fileprivate var myVoiceState: LNUserVoiceStateVO = LNUserVoiceStateVO()
    
    private var randomProfile: LNRandomProfileResponse?
    
    private let captchaCoolDown = 60
    private var captchaRemain = 0
    private var captchaTimer: Timer?
    var canSendCaptcha: Bool {
        captchaRemain == 0
    }
    
    private init() {
        LNEventDeliver.addObserver(self)
    }
}

extension LNProfileManager {
    func reloadMyProfile() {
        LNHttpManager.shared.getMyProfile { [weak self] res, err in
            guard let self else { return }
            guard err == nil, let res else { return }
            if let relation = res.userFollowStat {
                LNRelationManager.shared.updateMyRelationInfo(relation)
            }
            if let voice = res.userVoice {
                myVoiceState = voice
                notifyUserVoiceBarInfoChanged()
            }
        }
        getUserProfile(uid: myUid) { _ in }
    }
    
    func modifyMyProfile(config: LNProfileUpdateConfig, queue: DispatchQueue = .main,
                         handler: @escaping (Bool) -> Void) {
        LNHttpManager.shared.modifyMyProfile(
            config: config) { [weak self] err in
            guard let self else { return }
            if let err {
                showToast(err.errorDesc)
            } else {
                reloadMyProfile()
                if config.interest != nil {
                    LNGameMateManager.shared.getGameTypeList { _ in }
                }
            }
            queue.asyncIfNotGlobal {
                handler(err == nil)
            }
        }
    }
    
    func setMyVoiceBar(url: String, duration: Int, queue: DispatchQueue = .main,
                       handler: @escaping (Bool) -> Void) {
        LNHttpManager.shared.setMyVoiceBar(url: url, duration: duration) { [weak self] err in
            queue.asyncIfNotGlobal {
                handler(err == nil)
            }
            if let err {
                showToast(err.errorDesc)
            } else {
                guard let self else { return }
                reloadMyProfile()
            }
        }
    }
    
    func cleanVoiceBar(queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) {
        LNHttpManager.shared.cleanVoiceBar { [weak self] err in
            queue.asyncIfNotGlobal {
                handler(err == nil)
            }
            if let err {
                showToast(err.errorDesc)
            } else {
                guard let self else { return }
                reloadMyProfile()
            }
        }
    }
}

extension LNProfileManager {
    func getUserProfile(uid: String, queue: DispatchQueue = .main, handler: @escaping (LNUserProfileVO?) -> Void) {
        LNHttpManager.shared.getUsersInfo(uids: [uid]) { [weak self] res, err in
            guard let self else { return }
            if let res, err == nil {
                res.list.forEach {
                    if $0.userNo.isMyUid {
                        self.myUserInfo = $0
                    }
                    self.notifyUserInfoChanged(newInfo: $0)
                }
            } else {
                showToast(err?.errorDesc)
            }
            queue.asyncIfNotGlobal {
                handler(res?.list.first)
            }
        }
    }
    
    func getRandomProfile(queue: DispatchQueue = .main,
                          handler: @escaping (LNProfileRandomInfoVO?, LNProfileRandomInfoVO?) -> Void) {
        if let randomProfile {
            handler(randomProfile.male, randomProfile.female)
            return
        }
        LNHttpManager.shared.getRandomProfile { [weak self] res, err in
            if let self, let res {
                randomProfile = res
            }
            queue.asyncIfNotGlobal {
                handler(res?.male, res?.female)
            }
        }
    }
    
//    func getUserOnlineState(uids: [String], queue: DispatchQueue = .main, handler: @escaping ([String: Bool]) -> Void) {
//        LNHttpManager.shared.getUserOnlineState(uids: uids) { res, err in
//            queue.asyncIfNotGlobal {
//                handler(res?.list.reduce(into: [String: Bool](), { partialResult, state in
//                    partialResult[state.userNo] = state.online
//                }) ?? [:])
//            }
//        }
//    }
}

extension LNProfileManager {
    func getBindPhoneCaptcha(code: String, phone: String,
                             queue: DispatchQueue = .main,
                             handler: @escaping (Bool) -> Void) {
        LNHttpManager.shared.getBindPhoneCaptcha(code: code, phone: phone) { [weak self] err in
            queue.asyncIfNotGlobal {
                handler(err == nil)
            }
            guard let self else { return }
            if let err {
                showToast(err.errorDesc)
            } else {
                captchaRemain = captchaCoolDown
                notifyCaptchaTime(time: captchaRemain)
                startCaptchaTimer()
            }
        }
    }
    
    func bindPhone(code: String, phone: String,
                   queue: DispatchQueue = .main,
                   captcha: String, handler: @escaping (Bool) -> Void) {
        LNHttpManager.shared.bindPhone(code: code, phone: phone, captcha: captcha) { err in
            queue.asyncIfNotGlobal {
                handler(err == nil)
            }
            if let err {
                showToast(err.errorDesc)
            }
        }
    }
    
    private func startCaptchaTimer() {
        DispatchQueue.main.async { [weak self] in
            guard let self else { return }
            stopCaptchaTimer()
            
            let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
                guard let self else { return }
                captchaRemain -= 1
                notifyCaptchaTime(time: captchaRemain)
                if captchaRemain == 0 {
                    stopCaptchaTimer()
                }
            }
            RunLoop.main.add(timer, forMode: .common)
            captchaTimer = timer
        }
    }
    
    private func stopCaptchaTimer() {
        captchaTimer?.invalidate()
        captchaTimer = nil
    }
}

extension LNProfileManager {
    func reportCurrentLanguage(code: String,
                               queue: DispatchQueue = .main,
                               handler: @escaping (Bool) -> Void) {
        LNHttpManager.shared.reportCurrentLanguage(code: code) { err in
            queue.asyncIfNotGlobal {
                handler(err == nil)
            }
        }
    }
}

extension LNProfileManager: LNAccountManagerNotify {
    func onUserLogin() {
        reloadMyProfile()
    }
    
    func onUserLogout() {
        myUserInfo = LNUserProfileVO()
    }
}

extension LNProfileManager {
    private func notifyUserInfoChanged(newInfo: LNUserProfileVO) {
        LNEventDeliver.notifyEvent { ($0 as? LNProfileManagerNotify)?.onUserInfoChanged(userInfo: newInfo) }
    }
    
    private func notifyCaptchaTime(time: Int) {
        LNEventDeliver.notifyEvent {
            ($0 as? LNProfileManagerNotify)?.onBindPhoneCaptchaCoolDownChanged(time: time)
        }
    }
    
    private func notifyUserVoiceBarInfoChanged() {
        LNEventDeliver.notifyEvent { ($0 as? LNProfileManagerNotify)?.onUserVoiceBarInfoChanged() }
    }
}
