//
//  PlayerVC.swift
//  WOKA
//
//  Created by MacBook Pro on 30/05/24.


import UIKit
import JWPlayerKit
import AVKit

class PlayerVC: JWPlayerViewController, JWPlayerViewControllerDelegate {

    @IBOutlet weak var backButton: UIButton!
    var previousScale: CGFloat = 1.0
    
    var contentType : VideoContentType?
    
    var config: JWPlayerConfiguration!
    var dismissTapped: (() -> Void)?
    var videoIndex : Int?
    
    var errorCount = 0
    
    var isFullScreenBtn = false
    
    var vm = PlayerVM()
    
    deinit {
        NotificationCenter.default.removeObserver(self,name: UIApplication.didEnterBackgroundNotification, object: nil)
        NotificationCenter.default.removeObserver(self,name: UIApplication.willEnterForegroundNotification, object: nil)
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .allButUpsideDown
    }
    
    override var shouldAutorotate: Bool {
        return true
    }
    
    override var prefersStatusBarHidden: Bool {
        if #available(iOS 16.0, *) {
            // Code for iOS 16.0 and above
            return false
        } else {
            // Fallback code for earlier iOS versions
            return true
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        player.configurePlayer(with: config)
        vm.vc = self
        vm.initView()
        //bring back button to the front
        self.view.bringSubviewToFront(backButton)
        NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
        
            
        let skinStylingBuilder = JWPlayerSkinBuilder()
            .adCueColor(.yellow)
        //                    .buttonsColor(.blue)
        //                    .backgroundColor(.cyan)
        
        let skinStyling = try? skinStylingBuilder.build()
        self.styling = skinStyling
        self.setVisibility(.hidden, for: [.fullscreenButton, .pictureInPictureButton,.languagesButton])
        
        print("Quality:- ", player.visualQualityLevels)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        setNeedsStatusBarAppearanceUpdate()
    }

    // for ios 15 and below
    func rotateView(to angle: CGFloat) {
        // Apply rotation to the view's transform
        view.transform = CGAffineTransform(rotationAngle: angle)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        if #available(iOS 16.0, *) {
            // Code for iOS 15.0 and above
            print("Running on iOS 15.0 or later")
            DispatchQueue.main.async {
                appDelegate.deviceOrientation = .landscapeRight
                let value = UIInterfaceOrientation.landscapeRight.rawValue
                UIDevice.current.setValue(value, forKey: "orientation")
            }
        } else {
            // Fallback code for earlier iOS versions
            rotateView(to: .pi / 2) // Example: 90 degrees rotation
            print("Running on a version earlier than iOS 15.0")
        }
        
        //Disable Picture in Picture
        playerView.allowsPictureInPicturePlayback = false
        playerView.captionStyle = .none
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        player.stop()
    }
 
    @IBAction func backBtnTapped(_ sender: UIButton) {
        switch contentType {
        case .liveStream:
            if let postID = vm.videoIDs.first {
                let duration = DateFormatterLib.dateDifferenceINT(date1: vm.startTimeStamp, date2: Date())
                let totalDuration = duration + vm.totalVideoViewTime
                AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.liveTV.rawValue, duration: totalDuration, catID: 0) { _ in}
                vm.handleBackAction()
            }
        case .webSeries:
            if let catID = vm.catID, vm.currentPlayingIndex >= 0 && vm.currentPlayingIndex < (vm.videoIDs.count - 1) {
                let postID = vm.videoIDs[vm.currentPlayingIndex]
                let duration = DateFormatterLib.dateDifferenceINT(date1: vm.startTimeStamp, date2: Date())
                let totalDuration = duration + vm.totalVideoViewTime
                Utilities.startProgressHUD(msg: K.ConstantString.sync)
                AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.episode.rawValue, duration: totalDuration, catID: catID) { [weak self] isDone in
                    guard let self else{return}
                    if isDone{
                        Utilities.dismissProgressHUD()
                        vm.handleBackAction()
                        K.GVar.reloadContinueWebSeries = true
                    }else{
                        Utilities.dismissProgressHUD()
                        vm.handleBackAction()
                    }
                }
            } else {
                vm.handleBackAction()
            }
        case .trailer:
            let duration = DateFormatterLib.dateDifferenceINT(date1: vm.startTimeStamp, date2: Date())
            let totalDuration = duration + vm.totalVideoViewTime
            AuthFunc.shareInstance.userVideoView(postID: 0, postType: PostType.episode.rawValue, duration: totalDuration, catID: 0) { _ in}
            vm.handleBackAction()
        case .continueWatching:
            if let catID = vm.catID,let postID = vm.videoIDs.first{
                let duration = DateFormatterLib.dateDifferenceINT(date1: vm.startTimeStamp, date2: Date())
                let totalDuration = duration + vm.totalVideoViewTime
                Utilities.startProgressHUD(msg: K.ConstantString.sync)
                AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.episode.rawValue, duration: totalDuration, catID: catID) { [weak self] isDone in
                    guard let self else{return}
                    if isDone{
                        Utilities.dismissProgressHUD()
                        vm.handleBackAction()
                        K.GVar.reloadContinueWebSeries = true
                    }else{
                        Utilities.dismissProgressHUD()
                        vm.handleBackAction()
                    }
                }
            } else {
                vm.handleBackAction()
            }
        case .audioBooks:
            if let postID = vm.videoIDs.first {
                let duration = DateFormatterLib.dateDifferenceINT(date1: vm.startTimeStamp, date2: Date())
                let totalDuration = duration + vm.totalVideoViewTime
                Utilities.startProgressHUD(msg: K.ConstantString.sync)
                AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.audio.rawValue, duration: totalDuration, catID: 0) { [weak self] isDone in
                    guard let self else{
                        Utilities.dismissProgressHUD()
                        return
                    }
                    if isDone{
                        Utilities.dismissProgressHUD()
                        K.GVar.reloadContinueAudioBooks = true
                        vm.handleBackAction()
                    }else{
                        Utilities.dismissProgressHUD()
                        vm.handleBackAction()
                    }
                }
            }
        case .games:
            break
        case .songs:
            break
        default:
            vm.handleBackAction()
        }
        
    }
    
    // MARK: - App LifeCycle Handling

    @objc func appDidEnterBackground() {
        print("App entered background PlayerVC")
        player.pause()

        //Update uservideo view
        switch contentType {
        case .liveStream:
            vm.updateUserView()
        case .webSeries:
            vm.updateUserView()
        case .trailer:
            vm.updateUserView()
        case .continueWatching:
            vm.updateUserView()
        case .audioBooks:
            vm.updateUserView()
        case .games:
            break
        case .songs:
            break
        default:
            break
        }
    }
    
    @objc func appWillEnterForeground() {
        print("App will enter foreground PlayerVC")
        
        //Reset StartTimestamp
        vm.startTimeStamp = Date()
        
        player.play()
    }
    
    // MARK: - Handle Screen Transition
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        
        coordinator.animate(alongsideTransition: nil) { [weak self] _ in
            guard let self else{return}
            vm.checkOrientation()
        }
    }

    // MARK: - JWPlayerViewControllerDelegate
    
    override func jwplayerPlaylistHasCompleted(_ player: any JWPlayer) {
        super.jwplayerPlaylistHasCompleted(player)
        // if playlist is finised make sure to add it to user video view
        vm.updateUserView()
        print("PlayList Over", vm.currentPlayingIndex)
    }
        
    override func jwplayer(_ player: any JWPlayer, didLoadPlaylist playlist: [JWPlayerItem]) {
        super.jwplayer(player, didLoadPlaylist: playlist)
        print("Playlist loaded")
    }
    
    // this will give index
    override func jwplayer(_ player: any JWPlayer, didLoadPlaylistItem item: JWPlayerItem, at index: UInt) {
        super.jwplayer(player, didLoadPlaylistItem: item, at: index)
        print("didLoadPlaylistItem ", index)
        
        // if currentplaylist index is not set, and the video index that we got from back screen check with the load index
        if contentType == .webSeries{
            if let videoIndex,vm.currentPlayingIndex == -1 && (index != videoIndex){
                return
            }
            
            if vm.currentPlayingIndex != -1 && vm.currentPlayingIndex != index{
                // so update the loaded index and increase the current playing index
                vm.updateUserView()
                vm.currentPlayingIndex = Int(index)
            }else{
                vm.currentPlayingIndex = Int(index)
            }
        }
    }
    
    override func jwplayer(_ player: any JWPlayer, didFinishLoadingWithTime loadTime: TimeInterval) {
        super.jwplayer(player, didFinishLoadingWithTime: loadTime)
        print("LoadTime", loadTime)
//        if let videoIndex , contentType == .webSeries, videoIndex != 0{
//            player.nextUpPlaylistIndex = videoIndex
//            player.next()
//            self.videoIndex = nil
//        }
    }
    
    override func jwplayerIsReady(_ player: JWPlayer) {
        super.jwplayerIsReady(player)
        switch contentType {
        case .liveStream:
            player.play()
        case .webSeries:
            break
//            player.loadPlayerItemAt(index: videoIndex ?? 0)
//            self.player.play(relatedContent: videoIndex ?? 0)
//            self.player.loadPlayerItemAt(index: videoIndex ?? 0)
//            player.play()
//            if videoIndex == 0{
//                player.seek(to: 0)
//            }else{
//                player.nextUpPlaylistIndex = videoIndex ?? 0
//                player.next()
//            }
        case .trailer,.songs:
            break
        case .continueWatching,.audioBooks, .games:
            player.seek(to: 0)
            player.play()
        case nil:
            break
        }
        print("IsReady")
    }
    
    override func jwplayer(_ player: JWPlayer, failedWithSetupError code: UInt, message: String) {
        super.jwplayer(player, failedWithSetupError: code, message: message)
        print("Setup Error: \(code) - \(message)")
    }

    override func jwplayer(_ player: JWPlayer, failedWithError code: UInt, message: String) {
        super.jwplayer(player, failedWithError: code, message: message)
        print("Error: \(code) - \(message)")

        // try reconnect 3 times else show retry
        if errorCount < 4{
            errorCount += 1
            self.player.configurePlayer(with: config)
            self.player.play()
            return
        }
        DispatchQueue.main.async {
            Utilities.alertWithBtnCancelCompletion(title: "Error", msgBody: message, okBtnStr: "Connect", vc: self) { [weak self] isDone in
                guard let self else{
                    self?.vm.handleBackAction()
                    return
                }
                errorCount = 0

                if isDone{
                    self.player.configurePlayer(with: config)
                    self.player.play()
                }else{
                    self.vm.handleBackAction()
                }
            }
        }
    }
    
    override func jwplayer(_ player: JWPlayer, encounteredWarning code: UInt, message: String) {
        super.jwplayer(player, encounteredWarning: code, message: message)
        //Handle the reconnecting of video here
        
        print("Warning: \(code) - \(message)")
    }

    override func jwplayer(_ player: JWPlayer, encounteredAdError code: UInt, message: String) {
        super.jwplayer(player, encounteredAdError: code, message: message)
        print("Ad Error: \(code) - \(message)")
    }

    override func jwplayer(_ player: JWPlayer, encounteredAdWarning code: UInt, message: String) {
        super.jwplayer(player, encounteredAdWarning: code, message: message)
        print("Ad Warning: \(code) - \(message)")
    }

    override func jwplayer(_ player: JWPlayer, isBufferingWithReason reason: JWBufferReason) {
        super.jwplayer(player, isBufferingWithReason: reason)
        print("Buffering Reason:", reason)
    }
    
    //When Player is Paused
    override func jwplayer(_ player: JWPlayer, didPauseWithReason reason: JWPauseReason) {
        super.jwplayer(player, didPauseWithReason: reason)
        if reason == .interaction{
            vm.updateClicks()
        }
        // Implement custom behavior
    }
    
    //When Player is Play
    override func jwplayer(_ player: JWPlayer, isPlayingWithReason reason: JWPlayReason) {
        super.jwplayer(player, isPlayingWithReason: reason)
        if reason == .interaction{
            vm.updateClicks()
        }
    }
    
}

// MARK: - Full Screen Handling

extension PlayerVC {
   
    func playerViewControllerWillGoFullScreen(_ controller: JWPlayerViewController) -> JWFullScreenViewController? {
        print("playerViewControllerWillGoFullScreen")
        self.interfaceBehavior = .hidden
        self.player.stop()
        controller.player.stop()
        if contentType == .liveStream{
            self.dismissTapped?()
        }
        return nil
    }
        
    func playerViewControllerDidGoFullScreen(_ controller: JWPlayerViewController) {
        print("playerViewControllerDidGoFullScreen")
        self.player.pause()
        self.dismissFullScreen(animated: false)
        self.dismiss(animated: true)
        return
    }
    
    func playerViewControllerWillDismissFullScreen(_ controller: JWPlayerViewController) {
        switch contentType {
        case .audioBooks,.liveStream,.webSeries, .continueWatching, .trailer:
            vm.updateUserView()
        default:
            break
        }
        print("playerViewControllerWillDismissFullScreen")
    }
    
    func playerViewControllerDidDismissFullScreen(_ controller: JWPlayerViewController) {
        print("playerViewControllerDidDismissFullScreen")
        vm.updateClicks()

        if #available(iOS 16.0, *) {
            // Code for iOS 16.0 and above
            DispatchQueue.main.async {
                appDelegate.deviceOrientation = .portrait
                let value = UIInterfaceOrientation.portrait.rawValue
                UIDevice.current.setValue(value, forKey: "orientation")
                UIViewController.attemptRotationToDeviceOrientation()
            }
        } else {
            // Fallback code for earlier iOS versions
            DispatchQueue.main.async {
                self.dismiss(animated: true)
            }
        }
        
    }
}

// MARK: - Handle ADs

extension PlayerVC{
    // Reports when an event is emitted by the player.
//    override func jwplayer(_ player: JWPlayer, adEvent event: JWAdEvent) {
//        super.jwplayer(player, adEvent: event)
//
//        switch event.type {
//        case .adBreakStart:
//            print("The ad break has begun")
//        case .schedule:
//            print("The ad(s) has been scheduled")
//        case .request:
//            print("The ad has been requested")
//        case .started:
//            print("The ad playback has started")
//        case .impression:
//            print("The ad impression has been fulfilled")
//        case .meta:
//            print("The ad metadata is ready")
//        case .clicked:
//            print("The ad has been tapped")
//        case .pause:
//            print("The ad playback has been paused")
//        case .play:
//            print("The ad playback has been resumed")
//        case .skipped:
//            print("The ad has been skipped")
//        case .complete:
//            print("The ad playback has finished")
//        case .adBreakEnd:
//            print("The ad break has finished")
//        default:
//            break
//        }
//    }
//
//    // This method is triggered when a time event fires for a currently playing ad.
//    override func onAdTimeEvent(_ time: JWTimeData) {
//        super.onAdTimeEvent(time)
//
//        // If you are not interested in the ad time data, avoid overriding this method due to performance reasons.
//    }
//
//    // When the player encounters an ad warning within the SDK, this method is called on the delegate.
//    // Ad warnings do not prevent the ad from continuing to play.
//    override func jwplayer(_ player: JWPlayer, encounteredAdWarning code: UInt, message: String) {
//        super.jwplayer(player, encounteredAdWarning: code, message: message)
//
//        print("An ad warning has been encountered: (\(code))-\(message)")
//    }
//
//    // When the player encounters an ad error within the SDK, this method is called on the delegate.
//    // Ad errors prevent ads from playing, but do not prevent media playback from continuing.
//    override func jwplayer(_ player: JWPlayer, encounteredAdError code: UInt, message: String) {
//        super.jwplayer(player, encounteredAdError: code, message: message)
//
//        print("An ad error has been encountered: (\(code))-\(message)")
//    }
}


// MARK: - JWPlayerViewController Delegate Functions

extension PlayerVC {
    
    func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, controlBarVisibilityChanged isVisible: Bool, frame: CGRect) {
        self.backButton.isHidden = !isVisible
    }

    func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, sizeChangedFrom oldSize: CGSize, to newSize: CGSize) {
        // Handle size change if necessary
    }

    func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, screenTappedAt position: CGPoint) {
        // Handle screen tap if necessary
    }

    func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, relatedMenuOpenedWithItems items: [JWPlayerKit.JWPlayerItem], withMethod method: JWPlayerKit.JWRelatedInteraction) {
        print("Related items:", items)
    }

    func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, relatedMenuClosedWithMethod method: JWRelatedInteraction) {
        print("Related menu closed")
    }
    
    func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, relatedItemBeganPlaying item: JWPlayerKit.JWPlayerItem, atIndex index: Int, withMethod method: JWPlayerKit.JWRelatedMethod) {
        print("Item ", item, index)
    }
    
}
