import SceneKit
import GameKit

#if os(macOS)
    typealias SCNColor = NSColor
#else
    typealias SCNColor = UIColor
#endif

@MainActor
class GameController: NSObject, SCNSceneRendererDelegate, GKLocalPlayerListener, GKMatchmakerViewControllerDelegate, GKMatchDelegate {
    
    let sceneRenderer: SCNSceneRenderer
    var match: GKMatch?
    var connected = false
    
    init(sceneRenderer renderer: SCNSceneRenderer) {
        sceneRenderer = renderer
        super.init()
        sceneRenderer.delegate = self
        sceneRenderer.scene = SCNScene(named: "Art.scnassets/ship.scn")!
    }
    
    func highlightNodes(atPoint point: CGPoint) {
        connected.toggle()
        if connected {
            connect()
            sceneRenderer.scene!.rootNode.childNode(withName: "ship", recursively: false)!.childNodes[0].geometry!.firstMaterial!.emission.contents = SCNColor.green
        } else {
            disconnect()
            sceneRenderer.scene!.rootNode.childNode(withName: "ship", recursively: false)!.childNodes[0].geometry!.firstMaterial!.emission.contents = SCNColor.red
        }
    }
    
    func connect() {
        GKLocalPlayer.local.register(self)
        GKLocalPlayer.local.authenticateHandler = { [self] viewController, error in
            print("authenticateHandler")
            if let viewController = viewController {
                #if os(macOS)
                (sceneRenderer as! NSView).window!.contentViewController!.presentAsSheet(viewController)
                #else
                (sceneRenderer as! UIView).window!.rootViewController!.present(viewController, animated: true)
                #endif
            } else if let error = error {
                fatalError(error.localizedDescription)
            }
            let request = GKMatchRequest()
            request.maxPlayers = 2
            let viewController = GKMatchmakerViewController(matchRequest: request)!
            viewController.matchmakerDelegate = self
            #if os(macOS)
            GKDialogController.shared().parentWindow = (sceneRenderer as! NSView).window!
            GKDialogController.shared().present(viewController)
            #else
            (sceneRenderer as! UIView).window!.rootViewController!.present(viewController, animated: true)
            #endif
        }
    }
    
    func disconnect() {
        match?.delegate = nil
        match?.disconnect()
        GKLocalPlayer.local.unregisterListener(self)
    }
    
    nonisolated func player(_ player: GKPlayer, didAccept invite: GKInvite) {
        print("did accept invite")
        MainActor.assumeIsolated {
            guard let viewController = GKMatchmakerViewController(invite: invite) else {
                return
            }
            viewController.matchmakerDelegate = self
            #if os(macOS)
            GKDialogController.shared().parentWindow = (sceneRenderer as! NSView).window!
            GKDialogController.shared().present(viewController)
            #else
            (sceneRenderer as! UIView).window!.rootViewController!.present(viewController, animated: true)
            #endif
        }
    }
    
    nonisolated func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {
        print("cancelled")
        MainActor.assumeIsolated {
            #if os(macOS)
            GKDialogController.shared().dismiss(self)
            #else
            (sceneRenderer as! UIView).window!.rootViewController!.dismiss(animated: true)
            #endif
        }
    }
    
    nonisolated func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {
        print("did find match")
        MainActor.assumeIsolated {
            self.match = match
            match.delegate = self
        }
    }
    
    nonisolated func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) {
        print("matchmaker failed \(error.localizedDescription)")
    }
    
    nonisolated func match(_ match: GKMatch, didFailWithError error: (any Error)?) {
        print("match failed \(error?.localizedDescription ?? "nil")")
    }
    
    nonisolated func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) {
        print("player \(player.displayName) did change state \(state.rawValue)")
        Task {
            switch state {
            case .connected:
                break
            case .disconnected, .unknown:
                let matchRequest = GKMatchRequest()
                matchRequest.recipients = [player]
                do {
                    try await GKMatchmaker.shared().addPlayers(to: match, matchRequest: matchRequest)
                    print("reinvited \(player.displayName) successfully")
                } catch {
                    print("reinviting \(player.displayName) failed.\n\(error.localizedDescription)")
                }
            @unknown default:
                break
            }
        }
    }
    
    nonisolated func match(_ match: GKMatch, shouldReinviteDisconnectedPlayer player: GKPlayer) -> Bool {
        return false
    }
    
    nonisolated func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {
        print("did receive \(data)")
    }
    
}