//
//  SailassistApp.swift
//  Sailassist
//
//  Created by Mamoru Sugita on 2023/10/13.
//

import SwiftUI
import UserNotifications
import WindowsAzureMessaging
import SwiftSignalRClient
import Foundation

@main
struct SailassistApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
    @State private var isTermsAgree = Preferences.termsAgreeDate != nil
    @State private var isPrivAgree = Preferences.privacyPolicyAgreeDate != nil

    let locationViewModel = LocationViewModel()

    var body: some Scene {
        WindowGroup {
            if !isTermsAgree{
                VStack(spacing: 0){
                    if let htmlPath = Bundle.main.path(forResource: "terms", ofType: "html") {
                        WebView(isLoading: .constant(false), url: htmlPath)
                    }

                    YesNoButtons(yesTitle: "Accept", noTitle: "Cancel") {
                        Preferences.termsAgreeDate_Int64 = DateTextLib.Date2UnixTime(date: Date())
                        isTermsAgree = true
                    } noAction: {
                        exit(0)
                    }
                }
                .preferredColorScheme(.dark)

            }else if !isPrivAgree{
                VStack(spacing: 0){
                    if let htmlPath = Bundle.main.path(forResource: "privacy", ofType: "html") {
                        WebView(isLoading: .constant(false), url: htmlPath)
                    }
                    
                    YesNoButtons(yesTitle: "Accept", noTitle: "Cancel") {
                        Preferences.privacyPolicyAgreeDate_Int64 = DateTextLib.Date2UnixTime(date: Date())
                        isPrivAgree = true
                    } noAction: {
                        exit(0)
                    }
                        
                }
                .preferredColorScheme(.dark)
            } else {
                ContentView()
                    .environmentObject(locationViewModel)
            }
        }
    }
}

class NotificationTags: NSObject {
    func addTags() {
        MSNotificationHub.clearTags()
        if Preferences.shipId != 0 {
            let shipId = String(Preferences.shipId)
            let routeTag = "route-" + shipId
            let bamTag = "bam-" + shipId
            let taskAlertTag = "taskalert-" + shipId
            let sailassistTag = "sailassist-" + shipId
            let chatTag = "chat-" + shipId
            let emergencyTag = "emergency-" + shipId

            if Preferences.ChatNotification == false {
                MSNotificationHub.addTags([routeTag, bamTag, taskAlertTag, sailassistTag, emergencyTag])
            } else {
                MSNotificationHub.addTags([routeTag, bamTag, taskAlertTag, sailassistTag, emergencyTag, chatTag])
            }
        }
    }
}

var connection: HubConnection?
class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, MSInstallationLifecycleDelegate {
    @ObservedObject var msg = SharingData.message
    private var hubConnectionDelegate: HubConnectionDelegate?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print(debug: "called")

        //プッシュ通知の利用許可のリクエスト送信
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
            if error != nil {
                return
            }
            if granted {
                MSNotificationHub.setLifecycleDelegate(self)
                UNUserNotificationCenter.current().delegate = self
                MSNotificationHub.setDelegate(self)

                MSNotificationHub.start(connectionString: HttpRequestType.ConnectionString.rawValue, hubName: HttpRequestType.HubName.rawValue)
            }
        }
        DispatchQueue.main.async {
            application.registerForRemoteNotifications()
        }

        //ユニークな値の生成
        if Preferences.DeviceId == "" {
            Preferences.DeviceId = UUID().uuidString
        }

        //MARK: - SignalR初期化
        hubConnectionDelegate = ChatHubConnectionDelegate(app: self)
        connection = HubConnectionBuilder(url: URL(string : HttpRequestType.SignalR.rawValue)!)
            .withHubConnectionDelegate(delegate: hubConnectionDelegate!)
            .withAutoReconnect()
//            .withLogging(minLogLevel: .error)
            .withLogging(minLogLevel: .debug)
            .withHubConnectionOptions(configureHubConnectionOptions: {options in options.keepAliveInterval = 20 })
            .build()

        if let r_connection = connection {
            //Chat
            r_connection.on(method: "ChatMessage", callback: { (message: ResChatMessage) in
                self.handleChatMessage(message: message)
            })

            //Photo / Image
            r_connection.on(method: "chatMessage", callback: { (message: ResChatMessage) in
                self.handleChatMessage(message: message)
            })

            r_connection.on(method: "AckMessage", callback: { (message: ResAckMessage) in
                self.handleAckMessage(message: message)
            })

            r_connection.on(method: "ChatMode", callback: { (message: ResChatMode) in
                self.handleChatMode(message: message)
            })

            r_connection.start()
        }

        return true
    }

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        print(debug: "called")
        let config = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
        config.delegateClass = SceneDelegate.self
        return config
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        print(debug: "called \(userInfo)")

        // Forward to MSNotificationHub
        MSNotificationHub.didReceiveRemoteNotification(userInfo)

        // Complete handling the notificationposition
        completionHandler(.noData)
    }

    // Push通知を受信した時（サイレントプッシュ）
    func notificationHub(_ notificationHub: MSNotificationHub, didReceivePushNotification notification: MSNotificationHubMessage) {
        print(debug: "called")
        //        let title = notification.title ?? ""
        //        let body = notification.body ?? ""
        let userInfo = notification.userInfo
        print(debug: "called \(String(describing: userInfo))")

        if !userInfo!.isEmpty {
            let ecaList = GetEcaList()
            ecaList.start()
        }
    }

    // APNs 登録成功時に呼ばれる
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let token = deviceToken.map { String(format: "%.2hhx", $0) }.joined()
        print(debug: "デバイストークン : \(token)")
    }

    // APNs 登録失敗時に呼ばれる
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print(debug: "APNs 登録に失敗しました : \(error.localizedDescription)")
    }

    private func handleChatMessage(message: ResChatMessage) {
        print(debug: "called")
        let ownMsg = ChatMessage(shipId: message.shipId, messageId: message.messageId, type: message.type, time: message.time, location: message.location, from: message.from, fromId: message.fromId, mode: message.mode, message: message.message, stampId: message.stampId, viewer: [])
        self.msg.messages.append(ownMsg)
    }

    private func handleAckMessage(message: ResAckMessage) {
        print(debug: "called")
        let msgIndex = self.msg.messages.firstIndex(where: {$0.messageId == message.messageId})
        if let index = msgIndex {
            if let fromId = message.fromId {
                let viewer = Viewer(time: message.time, location: message.location, id: fromId)
                self.msg.messages[index].viewer.append(viewer)
            } else {
                let viewer = Viewer(time: message.time, location: message.location, id: "")
                self.msg.messages[index].viewer.append(viewer)
            }
        }
    }

    private func handleChatMode(message: ResChatMode) {
        print(debug: "called")
        if message.mode == 1 {
            self.msg.mode = true
        } else {
            self.msg.mode = false
        }

        let ownMsg = ChatMessage(shipId: message.shipId, messageId: UUID().uuidString, type: 3, time: DateTextLib.Date2ISO8601Text(Date()), location: message.location, from: nil, fromId: nil, mode: message.mode, message: nil, stampId: 0, viewer: [])
        self.msg.messages.append(ownMsg)
    }

    //アプリ終了時
    func applicationWillTerminate(_ aNotification: UIApplication) {
        if let r_connection = connection {
            r_connection.stop()
        }
    }

    func connectionDidStart() {
        print(debug: "connectionDidOpen")
    }

    func connectionDidFailToOpen(error: Error) {
        print(debug: "connectionDidFailToOpen:\(error)")
    }

    func connectionDidClose(error: Error?) {
        if let err = error {
            print(debug: "connectionDidClose:\(err)")
        }
    }

    func connectionWillReconnect(error: Error) {
        print(debug: "connectionWillReconnect:\(error)")
    }

    func connectionDidReconnect() {
        print(debug: "connectionDidReconnect")
    }
}

class SignalR: NSObject {
    @ObservedObject var msg = SharingData.message

    func chatMessage(message: String, completion: @escaping (_ error: Error?) -> Void) {
        var request = ReqMessage(shipId: Preferences.shipId, messageId: UUID().uuidString.lowercased())
        request.type = 0                    //0:テキスト, 1:スタンプ
        request.time = DateTextLib.Date2ISO8601Text(Date())
        request.location = 2                //1:Shore , 2:Ship
        request.from = Preferences.UserName //投稿者名
        request.fromId = String(SharingData.my.id) //ユーザーID
        if SharingData.message.mode {
            request.mode = 1
        } else {
            request.mode = 0
        }
        request.message = message
        request.stampId = 0                 //スタンプ番号 0:Fire～

        if message != "" {
            connection!.invoke(method: "ChatMessage", request, invocationDidComplete: completion)
        }
    }

    func ackMessage(messageId: String, completion: @escaping (_ error: Error?) -> Void) {
        var request = ReqAckMessage(shipId: Preferences.shipId, messageId: messageId)
        request.time = DateTextLib.Date2ISO8601Text(Date())
        request.location = 2                //1:Shore , 2:Ship
        request.fromId = String(SharingData.my.id) //ユーザーID
        if messageId != "" {
            connection!.invoke(method: "AckMessage", request, invocationDidComplete: completion)
        }
    }

    func chatMode(mode: Bool, completion: @escaping (_ error: Error?) -> Void) {
        print(debug: "chatMode \(mode)")
        var request = ReqChatModeMessage(shipId: Preferences.shipId)
        request.location = 2                //1:Shore , 2:Ship
        request.fromeId = String(SharingData.my.id) //ユーザーID
        if mode == true {
            request.mode = 1
        } else {
            request.mode = 0
        }
        connection!.invoke(method: "ChatMode", request, invocationDidComplete: completion)
    }

    func stopConnection() {
        let test = connection.debugDescription
        print(debug: "Test: Chat Message \(test)")

        connection!.stop()
    }

    func startConnection() {
        connection!.start()
    }
}

class ChatHubConnectionDelegate: HubConnectionDelegate {
    weak var app: AppDelegate?

    init(app: AppDelegate) {
        self.app = app
    }

    func connectionDidOpen(hubConnection: HubConnection) {
        app?.connectionDidStart()
    }

    func connectionDidFailToOpen(error: Error) {
        app?.connectionDidFailToOpen(error: error)
    }

    func connectionDidClose(error: Error?) {
        app?.connectionDidClose(error: error)
    }

    func connectionWillReconnect(error: Error) {
        app?.connectionWillReconnect(error: error)
    }

    func connectionDidReconnect() {
        app?.connectionDidReconnect()
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    // フォアグラウンド状態で通知を受信して表示する
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        if !userInfo.isEmpty {
            guard let arrAPS = userInfo["aps"] as? [String: Any] else {
                completionHandler([[.banner, .badge, .sound]])
                return
            }

            let arrCategory = arrAPS["category"] as? String ?? ""
            switch arrCategory {
            case "chat":
                guard let arrAlert = arrAPS["alert"] as? [String: Any] else {
                    completionHandler([[.banner, .badge, .sound]])
                    return
                }
                let subtitle = arrAlert["subtitle"] as? String ?? ""    //送信先名称
//                let strTitle = arrAlert["title"] as? String ?? ""       //船名
//                let strBody = arrAlert["body"] as? String ?? ""         //送信内容
//                print(debug: "called \(subtitle) \(strTitle) \(strBody)")

                //送信先名称が同一の場合は通知を出さない
                if subtitle == Preferences.UserName {
                    return
                } else {
                    DispatchQueue.main.async {
                        SharingData.message.viewCnt += 1
                    }
                }
            case "sailassist":
                DispatchQueue.main.async {
                    print(debug: "sailassist")
                    SharingData.pushHistory.viewCnt += 1
                    let getPushHistory = GetPushHistory()
                    getPushHistory.start()
                }
            case "bam":
                DispatchQueue.main.async {
                    print(debug: "bam")
                    SharingData.pushHistory.viewCnt += 1
                    let getPushHistory = GetPushHistory()
                    getPushHistory.start()
                }
            case "route":
                DispatchQueue.main.async {
                    print(debug: "route")
                    SharingData.pushHistory.viewCnt += 1
                    let getPushHistory = GetPushHistory()
                    getPushHistory.start()
                }
            case "emergency":
                DispatchQueue.main.async {
                    print(debug: "emergency")
                    SharingData.pushHistory.viewCnt += 1
                    let getPushHistory = GetPushHistory()
                    getPushHistory.start()
                }
            default:
                print(debug: "default")
            }
        }
        completionHandler([[.banner, .badge, .sound]])
    }

    // バックグラウンド状態で通知を受信して表示する
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        if !userInfo.isEmpty {
            guard let arrAPS = userInfo["aps"] as? [String: Any] else {
                completionHandler()
                return
            }

            let arrCategory = arrAPS["category"] as? String ?? ""
            switch arrCategory {
            case "chat":
                guard let arrAlert = arrAPS["alert"] as? [String: Any] else {
                    completionHandler()
                    return
                }
//                let subtitle = arrAlert["subtitle"] as? String ?? ""    //送信内容
//                let strTitle = arrAlert["title"] as? String ?? ""       //船名
//                let strBody = arrAlert["body"] as? String ?? ""         //送信先名称
//                print(debug: "called \(subtitle) \(strTitle) \(strBody)")
            case "sailassist":
                print(debug: "sailassist")
                SharingData.pushHistory.viewCnt += 1
                let getPushHistory = GetPushHistory()
                getPushHistory.start()
            case "bam":
                print(debug: "bam")
                SharingData.pushHistory.viewCnt += 1
                let getPushHistory = GetPushHistory()
                getPushHistory.start()
            case "route":
                print(debug: "route")
                SharingData.pushHistory.viewCnt += 1
                let getPushHistory = GetPushHistory()
                getPushHistory.start()
            case "emergency":
                print(debug: "emergency")
                SharingData.pushHistory.viewCnt += 1
                let getPushHistory = GetPushHistory()
                getPushHistory.start()
            default:
                print(debug: "default")
            }
        }
        completionHandler()
    }
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate, ObservableObject {
    weak var windowScene: UIWindowScene?
    var tabWindow: UIWindow?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        windowScene = scene as? UIWindowScene
    }

    func addTabBar(_ selectedTabModel: SelectedTabModel){
        guard let scene = windowScene else{
            return
        }
        
        let tabBarController = UIHostingController(rootView:
            CustomTabBar()
                .environmentObject(selectedTabModel)
                .frame(maxHeight: .infinity, alignment: .bottom)
        )
        
        tabBarController.view.backgroundColor = .clear
        
        let tabWindow = PassThroughWindow(windowScene: scene)
        tabWindow.rootViewController = tabBarController
        tabWindow.isHidden = false
        self.tabWindow = tabWindow
    }
}

class PassThroughWindow: UIWindow{
//    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
//        guard let view = super.hitTest(point, with: event) else { return nil}
//        return rootViewController?.view == view ? nil : view
//    }
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)

        if let view = view {
            print("Hit test view: \(view), at point: \(point)")
            print("View hierarchy: \(type(of: view)) -> \(view.superview)")
        } else {
            print("Hit test view: nil, at point: \(point)")
        }

        // rootViewController?.view がヒットしても、子ビューがある場合は透過しない
        if rootViewController?.view == view {
            // 子ビューが存在する場合はタッチイベントを処理
            if rootViewController?.view.subviews.contains(where: { $0.frame.contains(point) }) == true {
                return view
            }
            print("Background was hit, passing through")
            return nil
        }

        return view
    }
}

// MARK: コメント出力を消す
func print(debug: Any = "", function: String = #function, file: String = #file, line: Int = #line) {
    #if DEBUG
        var filename: NSString = file as NSString
        filename = filename.lastPathComponent as NSString
        // Swift.print("File: \(filename), Line: \(line), Func: \(function) \n\(debug)")

        let outstr = String( format: "APPDebug File: %@, Line: %d, Func: %@\n\(debug)", filename, line, function )
        // Swift.print( outstr ) NSLog がある時は、両方に出るのでいらない
        NSLog( outstr )     // デバッグコンソールにも出力
    #endif
}
