import Foundation
import UIKit
import UserNotifications
import ComposeApp

@MainActor
class PushNotificationManager: NSObject, ObservableObject {
    static let shared = PushNotificationManager()

    @Published var deviceToken: String?
    @Published var notificationPermissionGranted = false

    /// Pending task ID to navigate to (set when notification is tapped before app is ready)
    @Published var pendingNavigationTaskId: Int?
    @Published var pendingNavigationResidenceId: Int?
    @Published var pendingNavigationDocumentId: Int?

    private let registeredTokenKey = "com.honeydue.registeredDeviceToken"
    private let registeredUserIdKey = "com.honeydue.registeredUserId"

    /// The last token that was successfully registered with the backend
    private var lastRegisteredToken: String? {
        get { UserDefaults.standard.string(forKey: registeredTokenKey) }
        set { UserDefaults.standard.set(newValue, forKey: registeredTokenKey) }
    }

    /// The user ID associated with the last successful registration
    private var lastRegisteredUserId: String? {
        get { UserDefaults.standard.string(forKey: registeredUserIdKey) }
        set { UserDefaults.standard.set(newValue, forKey: registeredUserIdKey) }
    }

    override init() {
        super.init()
    }

    // MARK: - Permission Request

    func requestNotificationPermission() async -> Bool {
        let center = UNUserNotificationCenter.current()

        do {
            let granted = try await center.requestAuthorization(options: [.alert, .sound, .badge])
            notificationPermissionGranted = granted

            if granted {
                #if DEBUG
                print("✅ Notification permission granted")
                #endif
                // Register for remote notifications on main thread
                await MainActor.run {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            } else {
                #if DEBUG
                print("❌ Notification permission denied")
                #endif
            }

            return granted
        } catch {
            #if DEBUG
            print("❌ Error requesting notification permission: \(error)")
            #endif
            return false
        }
    }

    // MARK: - Token Management

    func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) {
        let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
        self.deviceToken = tokenString
        let redactedToken = "\(tokenString.prefix(8))...\(tokenString.suffix(8))"
        #if DEBUG
        print("📱 APNs device token: \(redactedToken)")
        #endif

        // Register with backend
        Task { [weak self] in
            guard let self else { return }
            await self.registerDeviceWithBackend(token: tokenString)
        }
    }

    func didFailToRegisterForRemoteNotifications(withError error: Error) {
        #if DEBUG
        print("❌ Failed to register for remote notifications: \(error)")
        #endif
    }

    // MARK: - Backend Registration

    /// Call this after login to register any pending device token
    func registerDeviceAfterLogin() {
        guard let token = deviceToken else {
            #if DEBUG
            print("⚠️ No device token available for registration")
            #endif
            return
        }

        Task { [weak self] in
            guard let self else { return }
            await self.registerDeviceWithBackend(token: token, force: false)
        }
    }

    /// Call this when app returns from background to check and register if needed
    func checkAndRegisterDeviceIfNeeded() {
        guard let token = deviceToken else {
            #if DEBUG
            print("⚠️ No device token available for registration check")
            #endif
            return
        }

        Task { [weak self] in
            guard let self else { return }
            await self.registerDeviceWithBackend(token: token, force: false)
        }
    }

    private func registerDeviceWithBackend(token: String, force: Bool = false) async {
        guard TokenStorage.shared.getToken() != nil else {
            #if DEBUG
            print("⚠️ No auth token available, will register device after login")
            #endif
            return
        }

        let currentUserId = await MainActor.run {
            DataManagerObservable.shared.currentUser.map { String($0.id) }
        }

        // Skip only if both token and user identity match.
        if !force, token == lastRegisteredToken {
            if let currentUserId, currentUserId == lastRegisteredUserId {
                #if DEBUG
                print("📱 Device token already registered for current user, skipping")
                #endif
                return
            }

            if currentUserId == nil, lastRegisteredUserId == nil {
                #if DEBUG
                print("📱 Device token already registered, skipping")
                #endif
                return
            }
        }

        // Get unique device identifier
        let deviceId = await MainActor.run {
            UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
        }
        let deviceName = await MainActor.run {
            UIDevice.current.name
        }

        let request = DeviceRegistrationRequest(
            deviceId: deviceId,
            registrationId: token,
            platform: "ios",
            name: deviceName
        )

        do {
            let result = try await APILayer.shared.registerDevice(request: request)

            if let success = result as? ApiResultSuccess<DeviceRegistrationResponse> {
                #if DEBUG
                if success.data != nil {
                    print("✅ Device registered successfully")
                } else {
                    print("✅ Device registration acknowledged")
                }
                #endif
                // Cache the token on successful registration
                await MainActor.run {
                    self.lastRegisteredToken = token
                    self.lastRegisteredUserId = currentUserId
                }
            } else if let error = ApiResultBridge.error(from: result) {
                #if DEBUG
                print("❌ Failed to register device: \(error.message)")
                #endif
            } else {
                #if DEBUG
                print("⚠️ Unexpected result type from device registration")
                #endif
            }
        } catch {
            #if DEBUG
            print("❌ Error registering device: \(error.localizedDescription)")
            #endif
        }
    }

    // MARK: - Handle Notifications

    func handleNotification(userInfo: [AnyHashable: Any]) {
        #if DEBUG
        print("📬 Received notification: \(redactedPayloadSummary(userInfo: userInfo))")
        #endif

        // Extract notification data
        if let notificationId = stringValue(for: "notification_id", in: userInfo) {
            #if DEBUG
            print("Notification ID: \(notificationId)")
            #endif

            // Mark as read when user taps notification
            Task { [weak self] in
                guard let self else { return }
                await self.markNotificationAsRead(notificationId: notificationId)
            }
        }

        if let type = userInfo["type"] as? String {
            #if DEBUG
            print("Notification type: \(type)")
            #endif
            handleNotificationType(type: type, userInfo: userInfo)
        }
    }

    /// Called when user taps the notification body (not an action button)
    func handleNotificationTap(userInfo: [AnyHashable: Any]) {
        #if DEBUG
        print("📬 Handling notification tap")
        #endif

        // Mark as read
        if let notificationId = stringValue(for: "notification_id", in: userInfo) {
            Task { [weak self] in
                guard let self else { return }
                await self.markNotificationAsRead(notificationId: notificationId)
            }
        }

        // Check subscription status to determine navigation
        // Allow navigation if: Pro user, OR limitations are disabled, OR subscription hasn't loaded yet
        let subscription = SubscriptionCacheWrapper.shared.currentSubscription
        let isPremium = SubscriptionCacheWrapper.shared.currentTier == "pro"
        let limitationsEnabled = subscription?.limitationsEnabled ?? false  // Default to false (allow) if not loaded
        let canNavigateToTask = isPremium || !limitationsEnabled

        #if DEBUG
        print("📬 Push nav check: isPremium=\(isPremium), limitationsEnabled=\(limitationsEnabled), canNavigate=\(canNavigateToTask), subscription=\(subscription != nil ? "loaded" : "nil")")
        #endif

        if canNavigateToTask {
            // Navigate to task detail
            if let taskId = intValue(for: "task_id", in: userInfo) {
                navigateToTask(taskId: taskId)
            } else if let type = userInfo["type"] as? String {
                // Fall back to type-specific routing for non-task notifications.
                handleNotificationType(type: type, userInfo: userInfo)
            } else {
                navigateToHome()
            }
        } else {
            // Free user with limitations enabled - navigate to home
            navigateToHome()
        }
    }

    /// Called when user selects an action button on the notification
    func handleNotificationAction(actionIdentifier: String, userInfo: [AnyHashable: Any]) {
        #if DEBUG
        print("🔘 Handling notification action: \(actionIdentifier)")
        #endif

        // Mark as read
        if let notificationId = stringValue(for: "notification_id", in: userInfo) {
            Task { [weak self] in
                guard let self else { return }
                await self.markNotificationAsRead(notificationId: notificationId)
            }
        }

        // Check subscription status
        // Allow actions if: Pro user, OR limitations are disabled, OR subscription hasn't loaded yet
        let subscription = SubscriptionCacheWrapper.shared.currentSubscription
        let isPremium = SubscriptionCacheWrapper.shared.currentTier == "pro"
        let limitationsEnabled = subscription?.limitationsEnabled ?? false  // Default to false (allow) if not loaded
        let canPerformAction = isPremium || !limitationsEnabled

        guard canPerformAction else {
            // Free user with limitations enabled shouldn't see actions, but if they somehow do, go to home
            navigateToHome()
            return
        }

        // Extract task ID
        guard let taskId = intValue(for: "task_id", in: userInfo) else {
            #if DEBUG
            print("❌ No task_id found in notification")
            #endif
            return
        }

        // Handle action
        switch actionIdentifier {
        case NotificationActionID.viewTask.rawValue:
            navigateToTask(taskId: taskId)

        case NotificationActionID.completeTask.rawValue:
            performCompleteTask(taskId: taskId)

        case NotificationActionID.markInProgress.rawValue:
            performMarkInProgress(taskId: taskId)

        case NotificationActionID.cancelTask.rawValue:
            performCancelTask(taskId: taskId)

        case NotificationActionID.uncancelTask.rawValue:
            performUncancelTask(taskId: taskId)

        case NotificationActionID.editTask.rawValue:
            navigateToEditTask(taskId: taskId)

        default:
            #if DEBUG
            print("⚠️ Unknown action: \(actionIdentifier)")
            #endif
            navigateToTask(taskId: taskId)
        }
    }

    private func handleNotificationType(type: String, userInfo: [AnyHashable: Any]) {
        switch type {
        case "task_due_soon", "task_overdue", "task_completed", "task_assigned":
            if let taskId = intValue(for: "task_id", in: userInfo) {
                #if DEBUG
                print("Task notification for task ID: \(taskId)")
                #endif
                navigateToTask(taskId: taskId)
            }

        case "residence_shared":
            if let residenceId = intValue(for: "residence_id", in: userInfo) {
                #if DEBUG
                print("Residence shared notification for residence ID: \(residenceId)")
                #endif
                navigateToResidence(residenceId: residenceId)
            } else {
                #if DEBUG
                print("Residence shared notification without residence ID")
                #endif
                navigateToResidencesTab()
            }

        case "warranty_expiring":
            if let documentId = intValue(for: "document_id", in: userInfo) {
                #if DEBUG
                print("Warranty expiring notification for document ID: \(documentId)")
                #endif
                navigateToDocument(documentId: documentId)
            } else {
                #if DEBUG
                print("Warranty expiring notification without document ID")
                #endif
                navigateToDocumentsTab()
            }

        default:
            #if DEBUG
            print("Unknown notification type: \(type)")
            #endif
        }
    }

    // MARK: - Task Actions

    private func performCompleteTask(taskId: Int) {
        #if DEBUG
        print("✅ Completing task \(taskId) from notification action")
        #endif
        Task { [weak self] in
            guard let _ = self else { return }
            do {
                // Quick complete without photos/notes
                let request = TaskCompletionCreateRequest(
                    taskId: Int32(taskId),
                    completedAt: nil,
                    notes: nil,
                    actualCost: nil,
                    rating: nil,
                    uploadIds: nil
                )
                let result = try await APILayer.shared.createTaskCompletion(request: request)

                if result is ApiResultSuccess<TaskCompletionResponse> {
                    #if DEBUG
                    print("✅ Task \(taskId) completed successfully")
                    #endif
                    // Post notification for UI refresh
                    await MainActor.run {
                        NotificationCenter.default.post(name: .taskActionCompleted, object: nil)
                    }
                } else if let error = ApiResultBridge.error(from: result) {
                    #if DEBUG
                    print("❌ Failed to complete task: \(error.message)")
                    #endif
                } else {
                    #if DEBUG
                    print("⚠️ Unexpected result while completing task \(taskId)")
                    #endif
                }
            } catch {
                #if DEBUG
                print("❌ Error completing task: \(error.localizedDescription)")
                #endif
            }
        }
    }

    private func performMarkInProgress(taskId: Int) {
        #if DEBUG
        print("🔄 Marking task \(taskId) as in progress from notification action")
        #endif
        Task { [weak self] in
            guard let _ = self else { return }
            do {
                let result = try await APILayer.shared.markInProgress(taskId: Int32(taskId))

                if result is ApiResultSuccess<TaskResponse> {
                    #if DEBUG
                    print("✅ Task \(taskId) marked as in progress")
                    #endif
                    await MainActor.run {
                        NotificationCenter.default.post(name: .taskActionCompleted, object: nil)
                    }
                } else if let error = ApiResultBridge.error(from: result) {
                    #if DEBUG
                    print("❌ Failed to mark task in progress: \(error.message)")
                    #endif
                } else {
                    #if DEBUG
                    print("⚠️ Unexpected result while marking task \(taskId) in progress")
                    #endif
                }
            } catch {
                #if DEBUG
                print("❌ Error marking task in progress: \(error.localizedDescription)")
                #endif
            }
        }
    }

    private func performCancelTask(taskId: Int) {
        #if DEBUG
        print("🚫 Cancelling task \(taskId) from notification action")
        #endif
        Task { [weak self] in
            guard let _ = self else { return }
            do {
                let result = try await APILayer.shared.cancelTask(taskId: Int32(taskId))

                if result is ApiResultSuccess<TaskResponse> {
                    #if DEBUG
                    print("✅ Task \(taskId) cancelled")
                    #endif
                    await MainActor.run {
                        NotificationCenter.default.post(name: .taskActionCompleted, object: nil)
                    }
                } else if let error = ApiResultBridge.error(from: result) {
                    #if DEBUG
                    print("❌ Failed to cancel task: \(error.message)")
                    #endif
                } else {
                    #if DEBUG
                    print("⚠️ Unexpected result while cancelling task \(taskId)")
                    #endif
                }
            } catch {
                #if DEBUG
                print("❌ Error cancelling task: \(error.localizedDescription)")
                #endif
            }
        }
    }

    private func performUncancelTask(taskId: Int) {
        #if DEBUG
        print("↩️ Uncancelling task \(taskId) from notification action")
        #endif
        Task { [weak self] in
            guard let _ = self else { return }
            do {
                let result = try await APILayer.shared.uncancelTask(taskId: Int32(taskId))

                if result is ApiResultSuccess<TaskResponse> {
                    #if DEBUG
                    print("✅ Task \(taskId) uncancelled")
                    #endif
                    await MainActor.run {
                        NotificationCenter.default.post(name: .taskActionCompleted, object: nil)
                    }
                } else if let error = ApiResultBridge.error(from: result) {
                    #if DEBUG
                    print("❌ Failed to uncancel task: \(error.message)")
                    #endif
                } else {
                    #if DEBUG
                    print("⚠️ Unexpected result while uncancelling task \(taskId)")
                    #endif
                }
            } catch {
                #if DEBUG
                print("❌ Error uncancelling task: \(error.localizedDescription)")
                #endif
            }
        }
    }

    // MARK: - Navigation

    private func navigateToTask(taskId: Int) {
        #if DEBUG
        print("📱 Navigating to task \(taskId)")
        #endif
        // Store pending navigation in case MainTabView isn't ready yet
        pendingNavigationTaskId = taskId
        NotificationCenter.default.post(
            name: .navigateToTask,
            object: nil,
            userInfo: ["taskId": taskId]
        )
    }

    /// Clear pending navigation after it has been handled
    func clearPendingNavigation() {
        pendingNavigationTaskId = nil
        pendingNavigationResidenceId = nil
        pendingNavigationDocumentId = nil
    }

    /// Clear device registration cache (call on logout/account switch).
    func clearRegistrationCache() {
        lastRegisteredToken = nil
        lastRegisteredUserId = nil
    }

    private func navigateToEditTask(taskId: Int) {
        #if DEBUG
        print("✏️ Navigating to edit task \(taskId)")
        #endif
        NotificationCenter.default.post(
            name: .navigateToEditTask,
            object: nil,
            userInfo: ["taskId": taskId]
        )
    }

    private func navigateToResidence(residenceId: Int) {
        #if DEBUG
        print("🏠 Navigating to residence \(residenceId)")
        #endif
        pendingNavigationResidenceId = residenceId
        NotificationCenter.default.post(
            name: .navigateToResidence,
            object: nil,
            userInfo: ["residenceId": residenceId]
        )
    }

    private func navigateToResidencesTab() {
        NotificationCenter.default.post(name: .navigateToResidence, object: nil)
    }

    private func navigateToDocument(documentId: Int) {
        #if DEBUG
        print("📄 Navigating to document \(documentId)")
        #endif
        pendingNavigationDocumentId = documentId
        NotificationCenter.default.post(
            name: .navigateToDocument,
            object: nil,
            userInfo: ["documentId": documentId]
        )
    }

    private func navigateToDocumentsTab() {
        NotificationCenter.default.post(name: .navigateToDocument, object: nil)
    }

    private func navigateToHome() {
        #if DEBUG
        print("🏠 Navigating to home")
        #endif
        pendingNavigationTaskId = nil
        pendingNavigationResidenceId = nil
        pendingNavigationDocumentId = nil
        NotificationCenter.default.post(name: .navigateToHome, object: nil)
    }

    private func stringValue(for key: String, in userInfo: [AnyHashable: Any]) -> String? {
        if let value = userInfo[key] as? String {
            return value.trimmingCharacters(in: CharacterSet(charactersIn: "\""))
        }
        if let value = userInfo[key] as? NSNumber {
            return value.stringValue
        }
        if let value = userInfo[key] as? Int {
            return String(value)
        }
        return nil
    }

    private func intValue(for key: String, in userInfo: [AnyHashable: Any]) -> Int? {
        guard let raw = stringValue(for: key, in: userInfo) else {
            return nil
        }
        return Int(raw)
    }

    private func redactedPayloadSummary(userInfo: [AnyHashable: Any]) -> String {
        let type = stringValue(for: "type", in: userInfo) ?? "unknown"
        let notificationId = stringValue(for: "notification_id", in: userInfo) ?? "missing"
        let taskId = stringValue(for: "task_id", in: userInfo) ?? "none"
        let residenceId = stringValue(for: "residence_id", in: userInfo) ?? "none"
        let documentId = stringValue(for: "document_id", in: userInfo) ?? "none"
        return "type=\(type), notification_id=\(notificationId), task_id=\(taskId), residence_id=\(residenceId), document_id=\(documentId)"
    }

    private func markNotificationAsRead(notificationId: String) async {
        guard TokenStorage.shared.getToken() != nil,
              let notificationIdInt = Int32(notificationId) else {
            return
        }

        do {
            let result = try await APILayer.shared.markNotificationAsRead(notificationId: notificationIdInt)

            if result is ApiResultSuccess<MessageResponse> {
                #if DEBUG
                print("✅ Notification marked as read")
                #endif
            } else if let error = ApiResultBridge.error(from: result) {
                #if DEBUG
                print("❌ Failed to mark notification as read: \(error.message)")
                #endif
            } else {
                #if DEBUG
                print("⚠️ Unexpected result while marking notification read")
                #endif
            }
        } catch {
            #if DEBUG
            print("❌ Error marking notification as read: \(error.localizedDescription)")
            #endif
        }
    }

    // MARK: - Notification Preferences

    func updateNotificationPreferences(_ preferences: UpdateNotificationPreferencesRequest) async -> Bool {
        guard TokenStorage.shared.getToken() != nil else {
            #if DEBUG
            print("⚠️ No auth token available")
            #endif
            return false
        }

        do {
            let result = try await APILayer.shared.updateNotificationPreferences(request: preferences)

            if result is ApiResultSuccess<NotificationPreference> {
                #if DEBUG
                print("✅ Notification preferences updated")
                #endif
                return true
            } else if let error = ApiResultBridge.error(from: result) {
                #if DEBUG
                print("❌ Failed to update preferences: \(error.message)")
                #endif
                return false
            }
            #if DEBUG
            print("⚠️ Unexpected result while updating notification preferences")
            #endif
            return false
        } catch {
            #if DEBUG
            print("❌ Error updating notification preferences: \(error.localizedDescription)")
            #endif
            return false
        }
    }

    func getNotificationPreferences() async -> NotificationPreference? {
        guard TokenStorage.shared.getToken() != nil else {
            #if DEBUG
            print("⚠️ No auth token available")
            #endif
            return nil
        }

        do {
            let result = try await APILayer.shared.getNotificationPreferences()

            if let success = result as? ApiResultSuccess<NotificationPreference> {
                return success.data
            } else if let error = ApiResultBridge.error(from: result) {
                #if DEBUG
                print("❌ Failed to get preferences: \(error.message)")
                #endif
                return nil
            }
            #if DEBUG
            print("⚠️ Unexpected result while loading notification preferences")
            #endif
            return nil
        } catch {
            #if DEBUG
            print("❌ Error getting notification preferences: \(error.localizedDescription)")
            #endif
            return nil
        }
    }

    // MARK: - Badge Management

    func clearBadge() {
        UNUserNotificationCenter.current().setBadgeCount(0)
    }

    func setBadge(count: Int) {
        UNUserNotificationCenter.current().setBadgeCount(count)
    }
}
