/**
 * DailyNotificationStorage.swift
 * 
 * Storage management for notification content and settings
 * Implements tiered storage: UserDefaults (quick) + CoreData (structured)
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

import Foundation
import CoreData

/**
 * Manages storage for notification content and settings
 * 
 * This class implements the tiered storage approach:
 * - Tier 1: UserDefaults for quick access to settings and recent data
 * - Tier 2: CoreData for structured notification content
 * - Tier 3: File system for large assets (future use)
 */
class DailyNotificationStorage {
    
    // MARK: - Constants
    
    private static let TAG = "DailyNotificationStorage"
    private static let PREFS_NAME = "DailyNotificationPrefs"
    private static let KEY_NOTIFICATIONS = "notifications"
    private static let KEY_SETTINGS = "settings"
    private static let KEY_LAST_FETCH = "last_fetch"
    private static let KEY_ADAPTIVE_SCHEDULING = "adaptive_scheduling"
    private static let KEY_LAST_SUCCESSFUL_RUN = "last_successful_run"
    private static let KEY_LAST_NOTIFY_EXECUTION = "last_notify_execution"
    private static let KEY_BGTASK_EARLIEST_BEGIN = "bgtask_earliest_begin"
    
    private static let MAX_CACHE_SIZE = 100 // Maximum notifications to keep
    private static let CACHE_CLEANUP_INTERVAL: TimeInterval = 24 * 60 * 60 // 24 hours
    
    // MARK: - Properties
    
    private let userDefaults: UserDefaults
    private let database: DailyNotificationDatabase
    private var notificationCache: [String: NotificationContent] = [:]
    private var notificationList: [NotificationContent] = []
    private let cacheQueue = DispatchQueue(label: "org.timesafari.dailynotification.storage.cache", attributes: .concurrent)
    
    // MARK: - Initialization
    
    /**
     * Initialize storage with database path
     * 
     * @param databasePath Path to SQLite database
     */
    init(databasePath: String? = nil) {
        self.userDefaults = UserDefaults.standard
        let path = databasePath ?? Self.getDefaultDatabasePath()
        self.database = DailyNotificationDatabase(path: path)
        
        loadNotificationsFromStorage()
        cleanupOldNotifications()
    }
    
    /**
     * Get default database path
     */
    private static func getDefaultDatabasePath() -> String {
        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        return documentsPath.appendingPathComponent("daily_notifications.db").path
    }
    
    /**
     * Get current database path
     * 
     * @return Database path
     */
    func getDatabasePath() -> String {
        return database.getPath()
    }
    
    // MARK: - Notification Content Management
    
    /**
     * Save notification content to storage
     * 
     * @param content Notification content to save
     */
    func saveNotificationContent(_ content: NotificationContent) {
        cacheQueue.async(flags: .barrier) {
            print("\(Self.TAG): Saving notification: \(content.id)")
            
            // Add to cache
            self.notificationCache[content.id] = content
            
            // Add to list and sort by scheduled time
            self.notificationList.removeAll { $0.id == content.id }
            self.notificationList.append(content)
            self.notificationList.sort { $0.scheduledTime < $1.scheduledTime }
            
            // Persist to UserDefaults
            self.saveNotificationsToStorage()
            
            // Persist to CoreData
            self.database.saveNotificationContent(content)
            
            print("\(Self.TAG): Notification saved successfully")
        }
    }
    
    /**
     * Get notification content by ID
     * 
     * @param id Notification ID
     * @return Notification content or nil if not found
     */
    func getNotificationContent(id: String) -> NotificationContent? {
        return cacheQueue.sync {
            return notificationCache[id]
        }
    }
    
    /**
     * Get the last notification that was delivered
     * 
     * @return Last notification or nil if none exists
     */
    func getLastNotification() -> NotificationContent? {
        return cacheQueue.sync {
            if notificationList.isEmpty {
                return nil
            }
            
            // Find the most recent delivered notification
            let currentTime = Int64(Date().timeIntervalSince1970 * 1000) // milliseconds
            for notification in notificationList.reversed() {
                if notification.scheduledTime <= currentTime {
                    return notification
                }
            }
            
            return nil
        }
    }
    
    /**
     * Get all notifications
     * 
     * @return Array of all notifications
     */
    func getAllNotifications() -> [NotificationContent] {
        return cacheQueue.sync {
            return Array(notificationList)
        }
    }
    
    /**
     * Get last rollover time for a notification ID
     * 
     * @param notificationId Notification ID
     * @return Last rollover time in milliseconds, or nil if never rolled over
     */
    func getLastRolloverTime(for notificationId: String) async -> Int64? {
        let key = "rollover_\(notificationId)"
        let lastTime = userDefaults.object(forKey: key) as? Int64
        return lastTime
    }
    
    /**
     * Save last rollover time for a notification ID
     * 
     * @param notificationId Notification ID
     * @param time Rollover time in milliseconds
     */
    func saveLastRolloverTime(for notificationId: String, time: Int64) async {
        let key = "rollover_\(notificationId)"
        userDefaults.set(time, forKey: key)
        userDefaults.synchronize()
    }
    
    /**
     * Get last rollover time (any notification)
     * 
     * @return Last rollover time in milliseconds, or 0 if never rolled over
     */
    func getLastRolloverTime() -> Int64 {
        let key = "rollover_last"
        return Int64(userDefaults.integer(forKey: key))
    }
    
    /**
     * Save last rollover time (any notification)
     * 
     * @param time Rollover time in milliseconds
     */
    func saveLastRolloverTime(_ time: Int64) {
        let key = "rollover_last"
        userDefaults.set(time, forKey: key)
        userDefaults.synchronize()
    }
    
    /**
     * Get notifications that are ready to be displayed
     * 
     * @return Array of ready notifications
     */
    func getReadyNotifications() -> [NotificationContent] {
        return cacheQueue.sync {
            let currentTime = Int64(Date().timeIntervalSince1970 * 1000) // milliseconds
            return notificationList.filter { $0.scheduledTime <= currentTime }
        }
    }
    
    /**
     * Delete notification content by ID
     * 
     * @param id Notification ID
     */
    func deleteNotificationContent(id: String) {
        cacheQueue.async(flags: .barrier) {
            print("\(Self.TAG): Deleting notification: \(id)")
            
            self.notificationCache.removeValue(forKey: id)
            self.notificationList.removeAll { $0.id == id }
            
            self.saveNotificationsToStorage()
            self.database.deleteNotificationContent(id: id)
            
            print("\(Self.TAG): Notification deleted successfully")
        }
    }
    
    /**
     * Clear all notification content
     */
    func clearAllNotifications() {
        cacheQueue.async(flags: .barrier) {
            print("\(Self.TAG): Clearing all notifications")
            
            self.notificationCache.removeAll()
            self.notificationList.removeAll()
            
            self.userDefaults.removeObject(forKey: Self.KEY_NOTIFICATIONS)
            self.database.clearAllNotifications()
            
            print("\(Self.TAG): All notifications cleared")
        }
    }
    
    // MARK: - Settings Management
    
    /**
     * Save settings
     * 
     * @param settings Settings dictionary
     */
    func saveSettings(_ settings: [String: Any]) {
        if let data = try? JSONSerialization.data(withJSONObject: settings) {
            userDefaults.set(data, forKey: Self.KEY_SETTINGS)
            print("\(Self.TAG): Settings saved")
        }
    }
    
    /**
     * Get settings
     * 
     * @return Settings dictionary or empty dictionary
     */
    func getSettings() -> [String: Any] {
        guard let data = userDefaults.data(forKey: Self.KEY_SETTINGS),
              let settings = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
            return [:]
        }
        return settings
    }
    
    // MARK: - Background Task Tracking
    
    /**
     * Save last successful BGTask run timestamp
     * 
     * @param timestamp Timestamp in milliseconds
     */
    func saveLastSuccessfulRun(timestamp: Int64) {
        userDefaults.set(timestamp, forKey: Self.KEY_LAST_SUCCESSFUL_RUN)
        print("\(Self.TAG): Last successful run saved: \(timestamp)")
    }
    
    /**
     * Get last successful BGTask run timestamp
     * 
     * @return Timestamp in milliseconds or nil
     */
    func getLastSuccessfulRun() -> Int64? {
        let timestamp = userDefaults.object(forKey: Self.KEY_LAST_SUCCESSFUL_RUN) as? Int64
        return timestamp
    }
    
    /**
     * Save last notify execution timestamp
     * 
     * @param timestamp Timestamp in milliseconds
     */
    func saveLastNotifyExecution(timestamp: Int64) {
        userDefaults.set(timestamp, forKey: Self.KEY_LAST_NOTIFY_EXECUTION)
        print("\(Self.TAG): Last notify execution saved: \(timestamp)")
    }
    
    /**
     * Get last notify execution timestamp
     * 
     * @return Timestamp in milliseconds or nil
     */
    func getLastNotifyExecution() -> Int64? {
        let timestamp = userDefaults.object(forKey: Self.KEY_LAST_NOTIFY_EXECUTION) as? Int64
        return timestamp
    }
    
    /**
     * Save BGTask earliest begin date
     * 
     * @param timestamp Timestamp in milliseconds
     */
    func saveBGTaskEarliestBegin(timestamp: Int64) {
        userDefaults.set(timestamp, forKey: Self.KEY_BGTASK_EARLIEST_BEGIN)
        print("\(Self.TAG): BGTask earliest begin saved: \(timestamp)")
    }
    
    /**
     * Get BGTask earliest begin date
     * 
     * @return Timestamp in milliseconds or nil
     */
    func getBGTaskEarliestBegin() -> Int64? {
        let timestamp = userDefaults.object(forKey: Self.KEY_BGTASK_EARLIEST_BEGIN) as? Int64
        return timestamp
    }
    
    // MARK: - Private Helper Methods
    
    /**
     * Load notifications from UserDefaults
     */
    private func loadNotificationsFromStorage() {
        guard let data = userDefaults.data(forKey: Self.KEY_NOTIFICATIONS),
              let notifications = try? JSONDecoder().decode([NotificationContent].self, from: data) else {
            print("\(Self.TAG): No notifications found in storage")
            return
        }
        
        cacheQueue.async(flags: .barrier) {
            self.notificationList = notifications
            for notification in notifications {
                self.notificationCache[notification.id] = notification
            }
            print("\(Self.TAG): Loaded \(notifications.count) notifications from storage")
        }
    }
    
    /**
     * Save notifications to UserDefaults
     */
    private func saveNotificationsToStorage() {
        guard let data = try? JSONEncoder().encode(notificationList) else {
            print("\(Self.TAG): Failed to encode notifications")
            return
        }
        userDefaults.set(data, forKey: Self.KEY_NOTIFICATIONS)
    }
    
    /**
     * Cleanup old notifications
     */
    private func cleanupOldNotifications() {
        cacheQueue.async(flags: .barrier) {
            let currentTime = Int64(Date().timeIntervalSince1970 * 1000) // milliseconds
            let cutoffTime = currentTime - Int64(Self.CACHE_CLEANUP_INTERVAL * 1000)
            
            self.notificationList.removeAll { notification in
                let isOld = notification.scheduledTime < cutoffTime
                if isOld {
                    self.notificationCache.removeValue(forKey: notification.id)
                }
                return isOld
            }
            
            // Limit cache size
            if self.notificationList.count > Self.MAX_CACHE_SIZE {
                let excess = self.notificationList.count - Self.MAX_CACHE_SIZE
                for i in 0..<excess {
                    let notification = self.notificationList[i]
                    self.notificationCache.removeValue(forKey: notification.id)
                }
                self.notificationList.removeFirst(excess)
            }
            
            self.saveNotificationsToStorage()
        }
    }
}

