import Flutter
import WidgetKit
import UIKit
import WatchConnectivity
import AppIntents
import flutter_app_intents

@main
@objc class AppDelegate: FlutterAppDelegate {
    let notifyDelegate = NotifyDelegate()
    private static var sharedWatchConnectivityService: WatchConnectivityService?

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        syncDefaultsToGroup()
        WidgetCenter.shared.reloadAllTimelines()
        
        UNUserNotificationCenter.current().delegate = notifyDelegate

        let replyableMessageCategory = UNNotificationCategory(
            identifier: "CHAT_MESSAGE",
            actions: [
                UNTextInputNotificationAction(
                    identifier: "reply_action",
                    title: "Reply",
                    options: []
                ),
            ],
            intentIdentifiers: [],
            options: []
        )
        UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
        
        GeneratedPluginRegistrant.register(with: self)
        
        // Setup widget sync method channel
        setupWidgetSyncChannel()
        
        // Always initialize and retain a strong reference
        if WCSession.isSupported() {
            AppDelegate.sharedWatchConnectivityService = WatchConnectivityService.shared
        } else {
            print("[iOS] WCSession not supported on this device.")
        }
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    private func setupWidgetSyncChannel() {
        let controller = window?.rootViewController as? FlutterViewController
        let channel = FlutterMethodChannel(name: "dev.solsynth.solian/widget", binaryMessenger: controller!.binaryMessenger)
        
        channel.setMethodCallHandler { [weak self] (call, result) in
            if call.method == "syncToWidget" {
                syncDefaultsToGroup()
                WidgetCenter.shared.reloadAllTimelines()
                result(true)
            } else {
                result(FlutterMethodNotImplemented)
            }
        }
    }
    
    override func applicationDidEnterBackground(_ application: UIApplication) {
        syncDefaultsToGroup()
        WidgetCenter.shared.reloadAllTimelines()
    }
    
    override func applicationWillTerminate(_ application: UIApplication) {
        syncDefaultsToGroup()
    }
}

final class WatchConnectivityService: NSObject, WCSessionDelegate {
    static let shared = WatchConnectivityService()
    private let session: WCSession = .default
    
    private override init() {
        super.init()
        print("[iOS] Activating WCSession...")
        session.delegate = self
        session.activate()
    }

    // MARK: - WCSessionDelegate

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if let error = error {
            print("[iOS] WCSession activation failed: \(error.localizedDescription)")
        } else {
            print("[iOS] WCSession activated with state: \(activationState.rawValue)")
            if activationState == .activated {
                sendDataToWatch()
            }
        }
    }

    func sessionDidBecomeInactive(_ session: WCSession) {}
    
    func sessionDidDeactivate(_ session: WCSession) {
        session.activate()
    }

    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
        print("[iOS] Received message: \(message)")
        if let request = message["request"] as? String, request == "data" {
            let token = UserDefaults.standard.getFlutterToken()
            let serverUrl = UserDefaults.standard.getServerUrl()
            
            var data: [String: Any] = ["serverUrl": serverUrl]
            if let token = token {
                data["token"] = token
            }
            
            print("[iOS] Replying with data: \(data)")
            replyHandler(data)
        }
    }

    func sendDataToWatch() {
        guard session.activationState == .activated else {
            return
        }
        
        let token = UserDefaults.standard.getFlutterToken()
        let serverUrl = UserDefaults.standard.getServerUrl()
        
        var data: [String: Any] = ["serverUrl": serverUrl]
        if let token = token {
            data["token"] = token
        }
        
        do {
            try session.updateApplicationContext(data)
            print("[iOS] Sent application context: \(data)")
        } catch {
            print("[iOS] Failed to send application context: \(error.localizedDescription)")
        }
    }
}

// MARK: - App Intents

@available(iOS 16.0, *)
struct OpenChatIntent: AppIntent {
    static var title: LocalizedStringResource = "Open Chat"
    static var description = IntentDescription("Open a specific chat room")
    static var isDiscoverable = true
    static var openAppWhenRun = true

    @Parameter(title: "Channel ID")
    var channelId: String?

    func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "open_chat",
            parameters: ["channelId": channelId ?? ""]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Chat opened"
            return .result(value: value)
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to open chat"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct OpenPostIntent: AppIntent {
    static var title: LocalizedStringResource = "Open Post"
    static var description = IntentDescription("Open a specific post")
    static var isDiscoverable = true
    static var openAppWhenRun = true

    @Parameter(title: "Post ID")
    var postId: String?

    func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "open_post",
            parameters: ["postId": postId ?? ""]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Post opened"
            return .result(value: value)
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to open post"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct OpenComposeIntent: AppIntent {
    static var title: LocalizedStringResource = "Open Compose"
    static var description = IntentDescription("Open compose post screen")
    static var isDiscoverable = true
    static var openAppWhenRun = true

    func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "open_compose",
            parameters: [:]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Compose screen opened"
            return .result(value: value)
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to open compose"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct ComposePostIntent: AppIntent {
    static var title: LocalizedStringResource = "Compose Post"
    static var description = IntentDescription("Create a new post")
    static var isDiscoverable = true
    static var openAppWhenRun = true

    func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "compose_post",
            parameters: [:]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Compose screen opened"
            return .result(value: value)
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to compose post"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct SearchContentIntent: AppIntent {
    static var title: LocalizedStringResource = "Search Content"
    static var description = IntentDescription("Search for content")
    static var isDiscoverable = true
    static var openAppWhenRun = true

    @Parameter(title: "Search Query")
    var query: String?

    func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "search_content",
            parameters: ["query": query ?? ""]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Search opened"
            return .result(value: value)
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to search"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct ViewNotificationsIntent: AppIntent {
    static var title: LocalizedStringResource = "View Notifications"
    static var description = IntentDescription("View notifications")
    static var isDiscoverable = true
    static var openAppWhenRun = true

    func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "view_notifications",
            parameters: [:]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Notifications opened"
            return .result(value: value)
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to view notifications"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct CheckNotificationsIntent: AppIntent {
    static var title: LocalizedStringResource = "Check Notifications"
    static var description = IntentDescription("Check notification count")
    static var isDiscoverable = true
    static var openAppWhenRun = false

    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "check_notifications",
            parameters: [:]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "You have new notifications"
            return .result(
                value: value,
                dialog: "\(value)"
            )
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to check notifications"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct SendMessageIntent: AppIntent {
    static var title: LocalizedStringResource = "Send Message"
    static var description = IntentDescription("Send a message to a chat channel")
    static var isDiscoverable = true
    static var openAppWhenRun = false

    @Parameter(title: "Channel ID")
    var channelId: String?

    @Parameter(title: "Message Content")
    var content: String?

    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
        guard let channelId = channelId, !channelId.isEmpty else {
            throw AppIntentError.executionFailed("Channel ID is required")
        }

        guard let content = content, !content.isEmpty else {
            throw AppIntentError.executionFailed("Message content is required")
        }

        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "send_message",
            parameters: ["channelId": channelId, "content": content]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Message sent"
            return .result(
                value: value,
                dialog: "\(value)"
            )
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to send message"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct ReadMessagesIntent: AppIntent {
    static var title: LocalizedStringResource = "Read Messages"
    static var description = IntentDescription("Read recent messages from a chat channel")
    static var isDiscoverable = true
    static var openAppWhenRun = false

    @Parameter(title: "Channel ID")
    var channelId: String?

    @Parameter(title: "Number of Messages", default: "5")
    var limit: String?

    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
        guard let channelId = channelId, !channelId.isEmpty else {
            throw AppIntentError.executionFailed("Channel ID is required")
        }

        let limit = limit ?? "5"
        var parameters: [String: Any] = ["channelId": channelId]
        parameters["limit"] = limit

        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "read_messages",
            parameters: parameters
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Messages retrieved"
            return .result(
                value: value,
                dialog: "\(value)"
            )
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to read messages"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct CheckUnreadChatsIntent: AppIntent {
    static var title: LocalizedStringResource = "Check Unread Chats"
    static var description = IntentDescription("Check number of unread chat messages")
    static var isDiscoverable = true
    static var openAppWhenRun = false

    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "check_unread_chats",
            parameters: [:]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "No unread messages"
            return .result(
                value: value,
                dialog: "\(value)"
            )
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to check unread chats"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

@available(iOS 16.0, *)
struct MarkNotificationsReadIntent: AppIntent {
    static var title: LocalizedStringResource = "Mark Notifications Read"
    static var description = IntentDescription("Mark all notifications as read")
    static var isDiscoverable = true
    static var openAppWhenRun = false

    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
        let plugin = FlutterAppIntentsPlugin.shared
        let result = await plugin.handleIntentInvocation(
            identifier: "mark_notifications_read",
            parameters: [:]
        )

        if let success = result["success"] as? Bool, success {
            let value = result["value"] as? String ?? "Notifications marked as read"
            return .result(
                value: value,
                dialog: "\(value)"
            )
        } else {
            let errorMessage = result["error"] as? String ?? "Failed to mark notifications as read"
            throw AppIntentError.executionFailed(errorMessage)
        }
    }
}

enum AppIntentError: Error {
    case executionFailed(String)
}

@available(iOS 16.0, *)
struct AppShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        return [
            // Open chat
            AppShortcut(
                intent: OpenChatIntent(),
                phrases: [
                    "Open chat with \(.applicationName)",
                    "Go to chat using \(.applicationName)",
                    "Show chat in \(.applicationName)"
                ]
            ),
            // Open post
            AppShortcut(
                intent: OpenPostIntent(),
                phrases: [
                    "Open post with \(.applicationName)",
                    "Show post using \(.applicationName)"
                ]
            ),
            // Compose
            AppShortcut(
                intent: OpenComposeIntent(),
                phrases: [
                    "Open compose with \(.applicationName)",
                    "New post using \(.applicationName)",
                    "Write post in \(.applicationName)"
                ]
            ),
            // Search
            AppShortcut(
                intent: SearchContentIntent(),
                phrases: [
                    "Search in \(.applicationName)",
                    "Find content using \(.applicationName)"
                ]
            ),
            // Check notifications
            AppShortcut(
                intent: CheckNotificationsIntent(),
                phrases: [
                    "Check notifications with \(.applicationName)",
                    "Get notifications using \(.applicationName)",
                    "Do I have notifications in \(.applicationName)"
                ]
            ),
            // Send message
            AppShortcut(
                intent: SendMessageIntent(),
                phrases: [
                    "Send message with \(.applicationName)",
                    "Post message using \(.applicationName)",
                    "Send text using \(.applicationName)"
                ]
            ),
            // Read messages
            AppShortcut(
                intent: ReadMessagesIntent(),
                phrases: [
                    "Read messages with \(.applicationName)",
                    "Get chat using \(.applicationName)",
                    "Show messages with \(.applicationName)"
                ]
            ),
            // Check unread chats
            AppShortcut(
                intent: CheckUnreadChatsIntent(),
                phrases: [
                    "Check unread chats with \(.applicationName)",
                    "Do I have messages using \(.applicationName)",
                    "Get unread messages with \(.applicationName)"
                ]
            ),
            // Mark notifications read
            AppShortcut(
                intent: MarkNotificationsReadIntent(),
                phrases: [
                    "Mark notifications read with \(.applicationName)",
                    "Clear notifications using \(.applicationName)",
                    "Mark all read with \(.applicationName)"
                ]
            )
        ]
    }
}
