import AVKit
import Defaults
import SwiftUI

#if !os(macOS)
    final class AppleAVPlayerViewControllerDelegate: NSObject, AVPlayerViewControllerDelegate {
        var player: PlayerModel { .shared }

        func playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart(_: AVPlayerViewController) -> Bool {
            false
        }

        #if os(iOS)
            func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
                let lockOrientation = player.rotateToLandscapeOnEnterFullScreen.interfaceOrientation
                if player.currentVideoIsLandscape {
                    if player.fullscreenInitiatedByButton {
                        Orientation.lockOrientation(player.isOrientationLocked
                            ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft)
                            : .landscape)
                    }
                    let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape
                        ? OrientationTracker.shared.currentInterfaceOrientation
                        : player.rotateToLandscapeOnEnterFullScreen.interfaceOrientation

                    Orientation.lockOrientation(
                        player.isOrientationLocked
                            ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft)
                            : .all,
                        andRotateTo: orientation
                    )
                }
            }

            func playerViewController(_: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
                let wasPlaying = player.isPlaying
                coordinator.animate(alongsideTransition: nil) { context in
                    if wasPlaying {
                        self.player.play()
                    }
                    if !context.isCancelled {
                        #if os(iOS)
                            if self.player.lockPortraitWhenBrowsing {
                                self.player.lockedOrientation = UIInterfaceOrientationMask.portrait
                            }
                            let rotationOrientation = self.player.lockPortraitWhenBrowsing ? UIInterfaceOrientation.portrait : nil
                            Orientation.lockOrientation(self.player.lockPortraitWhenBrowsing ? .portrait : .all, andRotateTo: rotationOrientation)

                            if wasPlaying {
                                self.player.play()
                            }

                            self.player.playingFullScreen = false
                        #endif
                    }
                }
            }

            func playerViewController(_: AVPlayerViewController, restoreUserInterfaceForFullScreenExitWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
                withAnimation(nil) {
                    player.presentingPlayer = true
                }

                completionHandler(true)
            }
        #endif

        func playerViewControllerWillStartPictureInPicture(_: AVPlayerViewController) {}

        func playerViewControllerWillStopPictureInPicture(_: AVPlayerViewController) {}

        func playerViewControllerDidStartPictureInPicture(_: AVPlayerViewController) {
            player.playingInPictureInPicture = true
            player.avPlayerBackend.startPictureInPictureOnPlay = false
            player.avPlayerBackend.startPictureInPictureOnSwitch = false
            player.controls.objectWillChange.send()

            if Defaults[.closePlayerOnOpeningPiP] { Delay.by(0.1) { self.player.hide() } }
        }

        func playerViewControllerDidStopPictureInPicture(_: AVPlayerViewController) {
            player.playingInPictureInPicture = false
            player.controls.objectWillChange.send()
        }

        func playerViewController(_: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
            player.presentingPlayer = true
            withAnimation(.linear(duration: 0.3)) {
                self.player.playingInPictureInPicture = false
                Delay.by(0.5) {
                    completionHandler(true)
                    Delay.by(0.2) {
                        self.player.play()
                    }
                }
            }
        }
    }
#endif

#if os(iOS)
    struct AppleAVPlayerView: UIViewControllerRepresentable {
        @State private var controller = AVPlayerViewController()

        func makeUIViewController(context _: Context) -> AVPlayerViewController {
            setupController()
            return controller
        }

        func updateUIViewController(_: AVPlayerViewController, context _: Context) {
            setupController()
        }

        func setupController() {
            controller.delegate = PlayerModel.shared.appleAVPlayerViewControllerDelegate
            controller.allowsPictureInPicturePlayback = true
            if #available(iOS 14.2, *) {
                controller.canStartPictureInPictureAutomaticallyFromInline = true
            }
            PlayerModel.shared.avPlayerBackend.controller = controller
        }
    }

    struct AppleAVPlayerLayerView: UIViewRepresentable {
        func makeUIView(context _: Context) -> some UIView {
            PlayerLayerView(frame: .zero)
        }

        func updateUIView(_: UIViewType, context _: Context) {}
    }

#elseif os(tvOS)
    struct AppleAVPlayerView: UIViewControllerRepresentable {
        func makeUIViewController(context _: Context) -> AppleAVPlayerViewController {
            let controller = AppleAVPlayerViewController()
            PlayerModel.shared.avPlayerBackend.controller = controller

            return controller
        }

        func updateUIViewController(_: AppleAVPlayerViewController, context _: Context) {
            PlayerModel.shared.rebuildTVMenu()
        }
    }
#else
    struct AppleAVPlayerView: NSViewRepresentable {
        func makeNSView(context _: Context) -> some NSView {
            let view = AVPlayerView()
            view.player = PlayerModel.shared.avPlayerBackend.avPlayer
            view.showsFullScreenToggleButton = true
            view.allowsPictureInPicturePlayback = true
            view.pictureInPictureDelegate = MacOSPiPDelegate.shared
            return view
        }

        func updateNSView(_: NSViewType, context _: Context) {}
    }

    struct AppleAVPlayerLayerView: NSViewRepresentable {
        func makeNSView(context _: Context) -> some NSView {
            PlayerLayerView(frame: .zero)
        }

        func updateNSView(_: NSViewType, context _: Context) {}
    }
#endif