/**
 * DailyNotificationTTLEnforcer.swift
 * 
 * iOS TTL-at-fire enforcement for notification freshness
 * Implements the skip rule: if (T - fetchedAt) > ttlSeconds → skip arming
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

import Foundation

/**
 * Enforces TTL-at-fire rules for notification freshness on iOS
 * 
 * This class implements the critical freshness enforcement:
 * - Before arming for T, if (T − fetchedAt) > ttlSeconds → skip
 * - Logs TTL violations for debugging
 * - Supports both SQLite and UserDefaults storage
 * - Provides freshness validation before scheduling
 */
class DailyNotificationTTLEnforcer {
    
    // MARK: - Constants
    
    private static let TAG = "DailyNotificationTTLEnforcer"
    private static let LOG_CODE_TTL_VIOLATION = "TTL_VIOLATION"
    
    // Default TTL values
    private static let DEFAULT_TTL_SECONDS: TimeInterval = 3600 // 1 hour
    private static let MIN_TTL_SECONDS: TimeInterval = 60 // 1 minute
    private static let MAX_TTL_SECONDS: TimeInterval = 86400 // 24 hours
    
    // MARK: - Properties
    
    private let database: DailyNotificationDatabase?
    private let useSharedStorage: Bool
    
    // MARK: - Initialization
    
    /**
     * Initialize TTL enforcer
     * 
     * @param database SQLite database (nil if using UserDefaults)
     * @param useSharedStorage Whether to use SQLite or UserDefaults
     */
    init(database: DailyNotificationDatabase?, useSharedStorage: Bool) {
        self.database = database
        self.useSharedStorage = useSharedStorage
        
        print("\(Self.TAG): TTL enforcer initialized with \(useSharedStorage ? "SQLite" : "UserDefaults")")
    }
    
    // MARK: - Freshness Validation
    
    /**
     * Check if notification content is fresh enough to arm
     * 
     * @param slotId Notification slot ID
     * @param scheduledTime T (slot time) - when notification should fire
     * @param fetchedAt When content was fetched
     * @return true if content is fresh enough to arm
     */
    func isContentFresh(slotId: String, scheduledTime: Date, fetchedAt: Date) -> Bool {
        do {
            let ttlSeconds = getTTLSeconds()
            
            // Calculate age at fire time
            let ageAtFireTime = scheduledTime.timeIntervalSince(fetchedAt)
            let ageAtFireSeconds = ageAtFireTime
            
            let isFresh = ageAtFireSeconds <= ttlSeconds
            
            if !isFresh {
                logTTLViolation(slotId: slotId, 
                              scheduledTime: scheduledTime, 
                              fetchedAt: fetchedAt, 
                              ageAtFireSeconds: ageAtFireSeconds, 
                              ttlSeconds: ttlSeconds)
            }
            
            print("\(Self.TAG): TTL check for \(slotId): age=\(Int(ageAtFireSeconds))s, ttl=\(Int(ttlSeconds))s, fresh=\(isFresh)")
            
            return isFresh
            
        } catch {
            print("\(Self.TAG): Error checking content freshness: \(error)")
            // Default to allowing arming if check fails
            return true
        }
    }
    
    /**
     * Check if notification content is fresh enough to arm (using stored fetchedAt)
     * 
     * @param slotId Notification slot ID
     * @param scheduledTime T (slot time) - when notification should fire
     * @return true if content is fresh enough to arm
     */
    func isContentFresh(slotId: String, scheduledTime: Date) -> Bool {
        do {
            guard let fetchedAt = getFetchedAt(slotId: slotId) else {
                print("\(Self.TAG): No fetchedAt found for slot: \(slotId)")
                return false
            }
            
            return isContentFresh(slotId: slotId, scheduledTime: scheduledTime, fetchedAt: fetchedAt)
            
        } catch {
            print("\(Self.TAG): Error checking content freshness for slot: \(slotId), error: \(error)")
            return false
        }
    }
    
    /**
     * Validate freshness before arming notification
     * 
     * @param notificationContent Notification content to validate
     * @return true if notification should be armed
     */
    func validateBeforeArming(_ notificationContent: NotificationContent) -> Bool {
        do {
            let slotId = notificationContent.id
            let scheduledTime = Date(timeIntervalSince1970: Double(notificationContent.scheduledTime) / 1000.0)
            let fetchedAt = Date(timeIntervalSince1970: Double(notificationContent.fetchedAt) / 1000.0)
            
            print("\(Self.TAG): Validating freshness before arming: slot=\(slotId), scheduled=\(scheduledTime), fetched=\(fetchedAt)")
            
            let isFresh = isContentFresh(slotId: slotId, scheduledTime: scheduledTime, fetchedAt: fetchedAt)
            
            if !isFresh {
                print("\(Self.TAG): Skipping arming due to TTL violation: \(slotId)")
                return false
            }
            
            print("\(Self.TAG): Content is fresh, proceeding with arming: \(slotId)")
            return true
            
        } catch {
            print("\(Self.TAG): Error validating freshness before arming: \(error)")
            return false
        }
    }
    
    // MARK: - TTL Configuration
    
    /**
     * Get TTL seconds from configuration
     * 
     * @return TTL in seconds
     */
    private func getTTLSeconds() -> TimeInterval {
        do {
            if useSharedStorage, let database = database {
                return getTTLFromSQLite(database: database)
            } else {
                return getTTLFromUserDefaults()
            }
        } catch {
            print("\(Self.TAG): Error getting TTL seconds: \(error)")
            return Self.DEFAULT_TTL_SECONDS
        }
    }
    
    /**
     * Get TTL from SQLite database
     * 
     * @param database SQLite database instance
     * @return TTL in seconds
     */
    private func getTTLFromSQLite(database: DailyNotificationDatabase) -> TimeInterval {
        do {
            // This would typically query the database for TTL configuration
            // For now, we'll return the default value
            let ttlSeconds = Self.DEFAULT_TTL_SECONDS
            
            // Validate TTL range
            let validatedTTL = max(Self.MIN_TTL_SECONDS, min(Self.MAX_TTL_SECONDS, ttlSeconds))
            
            return validatedTTL
            
        } catch {
            print("\(Self.TAG): Error getting TTL from SQLite: \(error)")
            return Self.DEFAULT_TTL_SECONDS
        }
    }
    
    /**
     * Get TTL from UserDefaults
     * 
     * @return TTL in seconds
     */
    private func getTTLFromUserDefaults() -> TimeInterval {
        do {
            let ttlSeconds = UserDefaults.standard.double(forKey: "ttlSeconds")
            let finalTTL = ttlSeconds > 0 ? ttlSeconds : Self.DEFAULT_TTL_SECONDS
            
            // Validate TTL range
            let validatedTTL = max(Self.MIN_TTL_SECONDS, min(Self.MAX_TTL_SECONDS, finalTTL))
            
            return validatedTTL
            
        } catch {
            print("\(Self.TAG): Error getting TTL from UserDefaults: \(error)")
            return Self.DEFAULT_TTL_SECONDS
        }
    }
    
    // MARK: - FetchedAt Retrieval
    
    /**
     * Get fetchedAt timestamp for a slot
     * 
     * @param slotId Notification slot ID
     * @return FetchedAt timestamp
     */
    private func getFetchedAt(slotId: String) -> Date? {
        do {
            if useSharedStorage, let database = database {
                return getFetchedAtFromSQLite(database: database, slotId: slotId)
            } else {
                return getFetchedAtFromUserDefaults(slotId: slotId)
            }
        } catch {
            print("\(Self.TAG): Error getting fetchedAt for slot: \(slotId), error: \(error)")
            return nil
        }
    }
    
    /**
     * Get fetchedAt from SQLite database
     * 
     * @param database SQLite database instance
     * @param slotId Notification slot ID
     * @return FetchedAt timestamp
     */
    private func getFetchedAtFromSQLite(database: DailyNotificationDatabase, slotId: String) -> Date? {
        do {
            // This would typically query the database for fetchedAt
            // For now, we'll return nil
            return nil
            
        } catch {
            print("\(Self.TAG): Error getting fetchedAt from SQLite: \(error)")
            return nil
        }
    }
    
    /**
     * Get fetchedAt from UserDefaults
     * 
     * @param slotId Notification slot ID
     * @return FetchedAt timestamp
     */
    private func getFetchedAtFromUserDefaults(slotId: String) -> Date? {
        do {
            let timestamp = UserDefaults.standard.double(forKey: "last_fetch_\(slotId)")
            return timestamp > 0 ? Date(timeIntervalSince1970: timestamp / 1000) : nil
            
        } catch {
            print("\(Self.TAG): Error getting fetchedAt from UserDefaults: \(error)")
            return nil
        }
    }
    
    // MARK: - TTL Violation Logging
    
    /**
     * Log TTL violation with detailed information
     * 
     * @param slotId Notification slot ID
     * @param scheduledTime When notification was scheduled to fire
     * @param fetchedAt When content was fetched
     * @param ageAtFireSeconds Age of content at fire time
     * @param ttlSeconds TTL limit in seconds
     */
    private func logTTLViolation(slotId: String, scheduledTime: Date, fetchedAt: Date, 
                               ageAtFireSeconds: TimeInterval, ttlSeconds: TimeInterval) {
        do {
            let violationMessage = String(format: "TTL violation: slot=%@, scheduled=%@, fetched=%@, age=%.0fs, ttl=%.0fs",
                                        slotId, scheduledTime.description, fetchedAt.description, ageAtFireSeconds, ttlSeconds)
            
            print("\(Self.TAG): \(Self.LOG_CODE_TTL_VIOLATION): \(violationMessage)")
            
            // Store violation for analytics
            storeTTLViolation(slotId: slotId, scheduledTime: scheduledTime, fetchedAt: fetchedAt, 
                            ageAtFireSeconds: ageAtFireSeconds, ttlSeconds: ttlSeconds)
            
        } catch {
            print("\(Self.TAG): Error logging TTL violation: \(error)")
        }
    }
    
    /**
     * Store TTL violation for analytics
     */
    private func storeTTLViolation(slotId: String, scheduledTime: Date, fetchedAt: Date, 
                                 ageAtFireSeconds: TimeInterval, ttlSeconds: TimeInterval) {
        do {
            if useSharedStorage, let database = database {
                storeTTLViolationInSQLite(database: database, slotId: slotId, scheduledTime: scheduledTime, 
                                        fetchedAt: fetchedAt, ageAtFireSeconds: ageAtFireSeconds, ttlSeconds: ttlSeconds)
            } else {
                storeTTLViolationInUserDefaults(slotId: slotId, scheduledTime: scheduledTime, fetchedAt: fetchedAt, 
                                              ageAtFireSeconds: ageAtFireSeconds, ttlSeconds: ttlSeconds)
            }
        } catch {
            print("\(Self.TAG): Error storing TTL violation: \(error)")
        }
    }
    
    /**
     * Store TTL violation in SQLite database
     */
    private func storeTTLViolationInSQLite(database: DailyNotificationDatabase, slotId: String, scheduledTime: Date, 
                                         fetchedAt: Date, ageAtFireSeconds: TimeInterval, ttlSeconds: TimeInterval) {
        do {
            // This would typically insert into the database
            // For now, we'll just log the action
            print("\(Self.TAG): Storing TTL violation in SQLite for slot: \(slotId)")
            
        } catch {
            print("\(Self.TAG): Error storing TTL violation in SQLite: \(error)")
        }
    }
    
    /**
     * Store TTL violation in UserDefaults
     */
    private func storeTTLViolationInUserDefaults(slotId: String, scheduledTime: Date, fetchedAt: Date, 
                                               ageAtFireSeconds: TimeInterval, ttlSeconds: TimeInterval) {
        do {
            let violationKey = "ttl_violation_\(slotId)_\(Int(scheduledTime.timeIntervalSince1970))"
            let violationValue = "\(Int(fetchedAt.timeIntervalSince1970 * 1000)),\(Int(ageAtFireSeconds)),\(Int(ttlSeconds)),\(Int(Date().timeIntervalSince1970 * 1000))"
            
            UserDefaults.standard.set(violationValue, forKey: violationKey)
            
        } catch {
            print("\(Self.TAG): Error storing TTL violation in UserDefaults: \(error)")
        }
    }
    
    // MARK: - Statistics
    
    /**
     * Get TTL violation statistics
     * 
     * @return Statistics string
     */
    func getTTLViolationStats() -> String {
        do {
            if useSharedStorage, let database = database {
                return getTTLViolationStatsFromSQLite(database: database)
            } else {
                return getTTLViolationStatsFromUserDefaults()
            }
        } catch {
            print("\(Self.TAG): Error getting TTL violation stats: \(error)")
            return "Error retrieving TTL violation statistics"
        }
    }
    
    /**
     * Get TTL violation statistics from SQLite
     */
    private func getTTLViolationStatsFromSQLite(database: DailyNotificationDatabase) -> String {
        do {
            // This would typically query the database for violation count
            // For now, we'll return a placeholder
            return "TTL violations: 0"
            
        } catch {
            print("\(Self.TAG): Error getting TTL violation stats from SQLite: \(error)")
            return "Error retrieving TTL violation statistics"
        }
    }
    
    /**
     * Get TTL violation statistics from UserDefaults
     */
    private func getTTLViolationStatsFromUserDefaults() -> String {
        do {
            let allKeys = UserDefaults.standard.dictionaryRepresentation().keys
            let violationCount = allKeys.filter { $0.hasPrefix("ttl_violation_") }.count
            
            return "TTL violations: \(violationCount)"
            
        } catch {
            print("\(Self.TAG): Error getting TTL violation stats from UserDefaults: \(error)")
            return "Error retrieving TTL violation statistics"
        }
    }
}
