import Foundation
import Combine
import UserNotifications
import UIKit

class NotificationService: ObservableObject {
    static let shared = NotificationService()

    @Published var isAuthorized = false
    @Published var pendingCount = 0

    private let center = UNUserNotificationCenter.current()

    private init() {
        checkAuthorizationStatus()
    }

    // MARK: - Authorization

    func checkAuthorizationStatus() {
        center.getNotificationSettings { [weak self] settings in
            DispatchQueue.main.async {
                self?.isAuthorized = settings.authorizationStatus == .authorized
            }
        }
    }

    func requestAuthorization() async -> Bool {
        do {
            let granted = try await center.requestAuthorization(options: [.alert, .badge, .sound])
            await MainActor.run {
                self.isAuthorized = granted
            }
            if granted {
                scheduleMonthlyPerformanceSummary()
            }
            return granted
        } catch {
            print("Notification authorization error: \(error)")
            return false
        }
    }

    // MARK: - Schedule Notifications

    func scheduleReminder(for source: InvestmentSource) {
        guard let nextDate = source.nextReminderDate else { return }
        guard source.frequency != .never else { return }

        // Remove existing notification for this source
        cancelReminder(for: source)

        // Get notification time from settings
        let settings = AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext)
        let notificationTime = settings.defaultNotificationTime ?? defaultNotificationTime()

        // Combine date and time
        let calendar = Calendar.current
        var components = calendar.dateComponents([.year, .month, .day], from: nextDate)
        let timeComponents = calendar.dateComponents([.hour, .minute], from: notificationTime)
        components.hour = timeComponents.hour
        components.minute = timeComponents.minute

        guard let triggerDate = calendar.date(from: components) else { return }

        // Create notification content
        let content = UNMutableNotificationContent()
        content.title = "Investment Update Reminder"
        content.body = "Time to update \(source.name). Tap to add a new snapshot."
        content.sound = .default
        content.badge = NSNumber(value: pendingCount + 1)
        content.userInfo = [
            "sourceId": source.id.uuidString,
            "sourceName": source.name
        ]

        // Create trigger
        let triggerComponents = calendar.dateComponents(
            [.year, .month, .day, .hour, .minute],
            from: triggerDate
        )
        let trigger = UNCalendarNotificationTrigger(dateMatching: triggerComponents, repeats: false)

        // Create request
        let request = UNNotificationRequest(
            identifier: notificationIdentifier(for: source),
            content: content,
            trigger: trigger
        )

        // Schedule
        center.add(request) { error in
            if let error = error {
                print("Failed to schedule notification: \(error)")
            } else {
                print("Scheduled reminder for \(source.name) on \(triggerDate)")
                FirebaseService.shared.logNotificationScheduled(frequency: source.notificationFrequency)
            }
        }
    }

    func scheduleAllReminders(for sources: [InvestmentSource]) {
        for source in sources {
            scheduleReminder(for: source)
        }
    }

    // MARK: - Cancel Notifications

    func cancelReminder(for source: InvestmentSource) {
        center.removePendingNotificationRequests(
            withIdentifiers: [notificationIdentifier(for: source)]
        )
    }

    func cancelAllReminders() {
        center.removeAllPendingNotificationRequests()
    }

    // MARK: - Badge Management

    func updateBadgeCount() {
        let repository = InvestmentSourceRepository()
        let needsUpdate = repository.fetchSourcesNeedingUpdate()
        pendingCount = needsUpdate.count

        center.setBadgeCount(pendingCount) { _ in }
    }

    func clearBadge() {
        pendingCount = 0
        center.setBadgeCount(0) { _ in }
    }

    // MARK: - Pending Notifications

    func getPendingNotifications() async -> [UNNotificationRequest] {
        await center.pendingNotificationRequests()
    }

    // MARK: - Helpers

    private func notificationIdentifier(for source: InvestmentSource) -> String {
        "investment_reminder_\(source.id.uuidString)"
    }

    private func defaultNotificationTime() -> Date {
        var components = DateComponents()
        components.hour = 9
        components.minute = 0
        return Calendar.current.date(from: components) ?? Date()
    }
}

// MARK: - Deep Link Handler

extension NotificationService {
    func handleNotificationResponse(_ response: UNNotificationResponse) {
        let userInfo = response.notification.request.content.userInfo

        // Handle batch update deep link from monthly check-in
        if let action = userInfo["action"] as? String, action == "batchUpdate" {
            NotificationCenter.default.post(name: .openBatchUpdate, object: nil)
            return
        }

        // Handle openDashboard deep link from monthly summary notification
        if let action = userInfo["action"] as? String, action == "openDashboard" {
            NotificationCenter.default.post(name: .openDashboard, object: nil)
            return
        }

        guard let sourceIdString = userInfo["sourceId"] as? String,
              let sourceId = UUID(uuidString: sourceIdString) else {
            return
        }

        // Post notification for deep linking
        NotificationCenter.default.post(
            name: .openSourceDetail,
            object: nil,
            userInfo: ["sourceId": sourceId]
        )
    }
}

// MARK: - Notification Names

extension Notification.Name {
    static let openSourceDetail = Notification.Name("openSourceDetail")
    static let didResetData = Notification.Name("didResetData")
    static let openBatchUpdate = Notification.Name("openBatchUpdate")
    static let openDashboard = Notification.Name("openDashboard")
    static let openQuickUpdate = Notification.Name("openQuickUpdate")
}

// MARK: - Re-engagement Notifications

extension NotificationService {
    /// Schedules a re-engagement notification 7 days from now.
    /// Call this every time the app becomes active to reset the timer.
    func scheduleReEngagementNotification() {
        guard isAuthorized else { return }

        center.removePendingNotificationRequests(withIdentifiers: ["re_engagement"])

        let content = UNMutableNotificationContent()
        content.title = String(localized: "reengagement_title")
        content.body = String(localized: "reengagement_body")
        content.sound = .default

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 7 * 24 * 3600, repeats: false)
        let request = UNNotificationRequest(identifier: "re_engagement", content: content, trigger: trigger)

        center.add(request) { error in
            if let error = error {
                print("Re-engagement notification error: \(error)")
            }
        }
    }

    /// Schedules a non-repeating monthly check-in notification for the 1st of the next month
    /// that doesn't have a completed check-in. Safe to call on every app activation.
    func scheduleMonthlyCheckIn() {
        guard isAuthorized else { return }

        let identifier = "monthly_checkin"
        center.removePendingNotificationRequests(withIdentifiers: [identifier])

        let calendar = Calendar.current
        let now = Date()
        guard let thisMonthStart = calendar.date(from: calendar.dateComponents([.year, .month], from: now)) else { return }

        // Walk forward from next month to find the first month whose check-in is not done
        for offset in 1...13 {
            guard let targetStart = calendar.date(byAdding: .month, value: offset, to: thisMonthStart) else { break }
            let isDone = MonthlyCheckInStore.completionDate(for: targetStart.adding(days: 1)) != nil
            if isDone { continue }

            var components = calendar.dateComponents([.year, .month], from: targetStart)
            components.day = 1
            components.hour = 9
            components.minute = 0

            let content = UNMutableNotificationContent()
            content.title = String(localized: "monthly_checkin_notification_title")
            content.body = String(localized: "monthly_checkin_notification_body")
            content.sound = .default
            content.userInfo = ["action": "batchUpdate"]

            let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
            let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)

            center.add(request) { error in
                if let error = error {
                    print("Monthly check-in notification error: \(error)")
                }
            }
            return
        }
    }

    /// Streak protection: reminds the user on the 25th of the CURRENT month if this
    /// month's check-in is still pending — loss aversion beats the day-1 nudge alone.
    /// Safe to call on every app activation (no-op if already done or date passed).
    func scheduleStreakProtectionReminder() {
        guard isAuthorized else { return }

        let identifier = "streak_protection"
        center.removePendingNotificationRequests(withIdentifiers: [identifier])

        let calendar = Calendar.current
        let now = Date()
        guard let thisMonthStart = calendar.date(from: calendar.dateComponents([.year, .month], from: now)) else { return }

        // Already checked in this month → nothing to protect
        guard MonthlyCheckInStore.completionDate(for: thisMonthStart.adding(days: 1)) == nil else { return }

        var components = calendar.dateComponents([.year, .month], from: thisMonthStart)
        components.day = 25
        components.hour = 18
        components.minute = 0
        guard let fireDate = calendar.date(from: components), fireDate > now else { return }

        let content = UNMutableNotificationContent()
        content.title = String(localized: "streak_protection_notification_title")
        content.body = String(localized: "streak_protection_notification_body")
        content.sound = .default
        content.userInfo = ["action": "batchUpdate"]

        let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
        let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)

        center.add(request) { error in
            if let error = error {
                print("Streak protection notification error: \(error)")
            }
        }
    }

    /// Schedules a monthly portfolio summary notification on the 5th of each month at 9am.
    func scheduleMonthlyPerformanceSummary() {
        guard isAuthorized else { return }

        let identifier = "monthly_summary"
        center.getPendingNotificationRequests { [weak self] requests in
            guard let self, !requests.contains(where: { $0.identifier == identifier }) else { return }

            let content = UNMutableNotificationContent()
            content.title = String(localized: "monthly_summary_notification_title")
            content.body = String(localized: "monthly_summary_notification_body")
            content.sound = .default
            content.userInfo = ["action": "openDashboard"]

            var components = DateComponents()
            components.day = 5
            components.hour = 9
            components.minute = 0

            let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
            let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)

            self.center.add(request) { error in
                if let error = error {
                    print("Monthly summary notification error: \(error)")
                }
            }
        }
    }

    /// Fires a one-time local notification celebrating goal achievement.
    func scheduleGoalAchievedNotification(goalName: String) {
        guard isAuthorized else { return }

        let content = UNMutableNotificationContent()
        content.title = String(localized: "goal_achieved_notification_title")
        content.body = String(format: String(localized: "goal_achieved_notification_body"), goalName)
        content.sound = .default

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
        let id = "goal_achieved_\(UUID().uuidString)"
        let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)

        center.add(request) { error in
            if let error = error {
                print("Goal achieved notification error: \(error)")
            }
        }
    }

    /// Fires a notification when the portfolio crosses a round milestone for the first time.
    func checkAndScheduleMilestoneNotification(portfolioValue: Decimal) {
        guard isAuthorized else { return }

        let milestones: [Decimal] = [1000, 2500, 5000, 10000, 25000, 50000,
                                      100000, 250000, 500000, 1_000_000]
        let notifiedKey = "lastNotifiedMilestone"
        let lastNotified = UserDefaults.standard.double(forKey: notifiedKey)
        let current = NSDecimalNumber(decimal: portfolioValue).doubleValue

        for milestone in milestones.reversed() {
            let ms = NSDecimalNumber(decimal: milestone).doubleValue
            if current >= ms {
                if ms > lastNotified {
                    UserDefaults.standard.set(ms, forKey: notifiedKey)
                    let milestoneStr = CurrencyFormatter.format(milestone, style: .currency, maximumFractionDigits: 0)

                    let content = UNMutableNotificationContent()
                    content.title = String(localized: "notification_milestone_title")
                    content.body = String(format: String(localized: "notification_milestone_body"), milestoneStr)
                    content.sound = .default
                    content.userInfo = ["action": "openDashboard"]

                    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
                    let request = UNNotificationRequest(
                        identifier: "portfolio_milestone_\(Int(ms))",
                        content: content,
                        trigger: trigger
                    )
                    center.add(request) { _ in }
                }
                break
            }
        }
    }
}

// MARK: - Background Refresh

extension NotificationService {
    func performBackgroundRefresh() {
        updateBadgeCount()

        // Reschedule any missed notifications
        let repository = InvestmentSourceRepository()
        let sources = repository.fetchActiveSources()
        scheduleAllReminders(for: sources)
    }
}
