import CoreMedia
import Foundation
import Siesta

struct OpenURLHandler {
    static var firstHandle = true
    static let yatteeProtocol = "yattee://"

    var accounts: AccountsModel { .shared }
    var navigation: NavigationModel { .shared }
    var recents: RecentsModel { .shared }
    var player: PlayerModel { .shared }
    var search: SearchModel { .shared }
    var navigationStyle: NavigationStyle

    func handle(_ url: URL) {
        if Self.firstHandle {
            Self.firstHandle = false

            Delay.by(1) { handle(url) }
            return
        }

        if accounts.current.isNil {
            accounts.setCurrent(accounts.any)
        }

        guard !accounts.current.isNil else {
            return
        }

        #if os(macOS)
            guard url.host != Windows.player.location else {
                return
            }
        #endif

        guard let url = url.byReplacingYatteeProtocol() else { return }

        let parser = URLParser(url: url)

        switch parser.destination {
        case .fileURL:
            handleFileURLOpen(parser)
        case .video:
            handleVideoUrlOpen(parser)
        case .playlist:
            handlePlaylistUrlOpen(parser)
        case .channel:
            handleChannelUrlOpen(parser)
        case .search:
            handleSearchUrlOpen(parser)
        case .favorites:
            navigation.hideViewsAboveBrowser()
            navigation.tabSelection = .home
            #if os(macOS)
                focusMainWindow()
            #endif
        case .subscriptions:
            guard accounts.app.supportsSubscriptions, accounts.signedIn else { return }
            navigation.hideViewsAboveBrowser()
            navigation.tabSelection = .subscriptions
            #if os(macOS)
                focusMainWindow()
            #endif
        case .popular:
            guard accounts.app.supportsPopular else { return }
            navigation.hideViewsAboveBrowser()
            navigation.tabSelection = .popular
            #if os(macOS)
                focusMainWindow()
            #endif
        case .trending:
            navigation.hideViewsAboveBrowser()
            navigation.tabSelection = .trending
            #if os(macOS)
                focusMainWindow()
            #endif
        default:
            navigation.presentAlert(title: "Error", message: "This URL could not be opened")
            #if os(macOS)
                guard !Windows.main.isOpen else { return }
                navigation.presentingAlertInVideoPlayer = true
            #endif
        }
    }

    private func handleFileURLOpen(_ parser: URLParser) {
        guard let url = parser.fileURL else { return }

        OpenVideosModel.shared.openURLs([url], removeQueueItems: false, playbackMode: .playNow)
    }

    private func handleVideoUrlOpen(_ parser: URLParser) {
        guard let id = parser.videoID else {
            navigation.presentAlert(title: "Could not open video", message: "Could not extract video ID")
            return
        }

        guard id != player.currentVideo?.id else {
            return
        }

        #if os(macOS)
            Windows.main.open()
        #endif

        let video = Video(app: accounts.current.app!, videoID: id)
        player.videoBeingOpened = .init(app: accounts.current.app!, videoID: id, title: "Loading video...")
        player.show()

        player
            .playerAPI(video)?
            .video(id)
            .load()
            .onSuccess { response in
                if let video: Video = response.typedContent() {
                    let time = parser.time.isNil ? nil : CMTime.secondsInDefaultTimescale(TimeInterval(parser.time!))
                    Delay.by(0.5) {
                        self.player.playNow(video, at: time)
                    }
                } else {
                    navigation.presentAlert(title: "Error", message: "This video could not be opened")
                }
            }
            .onFailure { responseError in
                navigation.presentAlert(title: "Could not open video", message: responseError.userMessage)
            }
    }

    private func handlePlaylistUrlOpen(_ parser: URLParser) {
        #if os(macOS)
            if alertIfNoMainWindowOpen() { return }
        #endif

        guard let playlistID = parser.playlistID else {
            navigation.presentAlert(title: "Could not open playlist", message: "Could not extract playlist ID")
            return
        }

        accounts.api.channelPlaylist(playlistID)?
            .load()
            .onSuccess { response in
                if var playlist: ChannelPlaylist = response.typedContent() {
                    playlist.id = playlistID
                    DispatchQueue.main.async {
                        NavigationModel.shared.openChannelPlaylist(
                            playlist,
                            navigationStyle: navigationStyle
                        )
                    }
                } else {
                    navigation.presentAlert(title: "Could not open playlist", message: "Playlist could not be found")
                }
            }
            .onFailure { responseError in
                navigation.presentAlert(title: "Could not open playlist", message: responseError.userMessage)
            }
    }

    private func handleChannelUrlOpen(_ parser: URLParser) {
        #if os(macOS)
            if alertIfNoMainWindowOpen() { return }
        #endif

        guard let resource = resourceForChannelUrl(parser) else {
            navigation.presentAlert(title: "Could not open channel", message: "Could not extract channel information")
            return
        }

        resource
            .load()
            .onSuccess { response in
                if let page: ChannelPage = response.typedContent(),
                   let channel = page.channel
                {
                    DispatchQueue.main.async {
                        NavigationModel.shared.openChannel(
                            channel,
                            navigationStyle: navigationStyle
                        )
                    }
                } else {
                    navigation.presentAlert(title: "Could not open channel", message: "Channel could not be found")
                }
            }
            .onFailure { responseError in
                navigation.presentAlert(title: "Could not open channel", message: responseError.userMessage)
            }
    }

    private func resourceForChannelUrl(_ parser: URLParser) -> Resource? {
        if let id = parser.channelID {
            return accounts.api.channel(id, contentType: .videos)
        }

        if let resource = resourceForUsernameUrl(parser) {
            return resource
        }

        guard let name = parser.channelName else {
            return nil
        }

        if accounts.app.supportsOpeningChannelsByName {
            return accounts.api.channelByName(name)
        }

        if let instance = InstancesModel.shared.all.first(where: { $0.app.supportsOpeningChannelsByName }) {
            return instance.anonymous.channelByName(name)
        }

        return nil
    }

    private func resourceForUsernameUrl(_ parser: URLParser) -> Resource? {
        guard let username = parser.username else { return nil }

        if accounts.app.supportsOpeningChannelsByName {
            return accounts.api.channelByUsername(username)
        }

        if let instance = InstancesModel.shared.all.first(where: { $0.app.supportsOpeningChannelsByName }) {
            return instance.anonymous.channelByUsername(username)
        }

        return nil
    }

    private func handleSearchUrlOpen(_ parser: URLParser) {
        #if os(macOS)
            if alertIfNoMainWindowOpen() { return }
        #endif

        NavigationModel.shared.openSearchQuery(parser.searchQuery)

        #if os(macOS)
            focusMainWindow()
        #endif
    }

    #if os(macOS)
        private func focusMainWindow() {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                Windows.main.focus()
            }
        }

        private func alertIfNoMainWindowOpen() -> Bool {
            guard !Windows.main.isOpen else {
                return false
            }

            navigation.presentAlert(
                title: "Restart the app to open this link",
                message:
                "To open this link in the app you need to close and open it manually to have browser window, " +
                    "then you can try opening links again.\n\nThis is a limitation of SwiftUI on macOS versions earlier than Ventura."
            )

            navigation.presentingAlertInVideoPlayer = true

            return true
        }
    #endif
}