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

package org.timesafari.dailynotification;

import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import java.util.concurrent.TimeUnit;

/**
 * Enforces TTL-at-fire rules for notification freshness
 * 
 * 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 SharedPreferences storage
 * - Provides freshness validation before scheduling
 */
public class DailyNotificationTTLEnforcer {
    
    private static final String TAG = "DailyNotificationTTLEnforcer";
    private static final String LOG_CODE_TTL_VIOLATION = "TTL_VIOLATION";
    
    // Default TTL values
    private static final long DEFAULT_TTL_SECONDS = 90000; // 25 hours (for daily notifications)
    private static final long MIN_TTL_SECONDS = 60; // 1 minute
    private static final long MAX_TTL_SECONDS = 172800; // 48 hours
    
    private final Context context;
    // Legacy SQLite helper reference (now removed). Keep as Object for compatibility; not used.
    private final Object database;
    private final boolean useSharedStorage;
    
    /**
     * Constructor
     * 
     * @param context Application context
     * @param database SQLite database (null if using SharedPreferences)
     * @param useSharedStorage Whether to use SQLite or SharedPreferences
     */
    public DailyNotificationTTLEnforcer(Context context, Object database, boolean useSharedStorage) {
        this.context = context;
        this.database = database;
        this.useSharedStorage = useSharedStorage;
    }
    
    /**
     * 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
     */
    public boolean isContentFresh(String slotId, long scheduledTime, long fetchedAt) {
        try {
            long ttlSeconds = getTTLSeconds();
            
            // Calculate age at fire time
            long ageAtFireTime = scheduledTime - fetchedAt;
            long ageAtFireSeconds = TimeUnit.MILLISECONDS.toSeconds(ageAtFireTime);
            
            boolean isFresh = ageAtFireSeconds <= ttlSeconds;
            
            if (!isFresh) {
                logTTLViolation(slotId, scheduledTime, fetchedAt, ageAtFireSeconds, ttlSeconds);
            }
            
            Log.d(TAG, String.format("TTL check for %s: age=%ds, ttl=%ds, fresh=%s", 
                slotId, ageAtFireSeconds, ttlSeconds, isFresh));
            
            return isFresh;
            
        } catch (Exception e) {
            Log.e(TAG, "Error checking content freshness", e);
            // 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
     */
    public boolean isContentFresh(String slotId, long scheduledTime) {
        try {
            long fetchedAt = getFetchedAt(slotId);
            if (fetchedAt == 0) {
                Log.w(TAG, "No fetchedAt found for slot: " + slotId);
                return false;
            }
            
            return isContentFresh(slotId, scheduledTime, fetchedAt);
            
        } catch (Exception e) {
            Log.e(TAG, "Error checking content freshness for slot: " + slotId, e);
            return false;
        }
    }
    
    /**
     * Validate freshness before arming notification
     * 
     * @param notificationContent Notification content to validate
     * @return true if notification should be armed
     */
    public boolean validateBeforeArming(NotificationContent notificationContent) {
        try {
            String slotId = notificationContent.getId();
            long scheduledTime = notificationContent.getScheduledTime();
            long fetchedAt = notificationContent.getFetchedAt();
            
            Log.d(TAG, String.format("Validating freshness before arming: slot=%s, scheduled=%d, fetched=%d", 
                slotId, scheduledTime, fetchedAt));
            
            boolean isFresh = isContentFresh(slotId, scheduledTime, fetchedAt);
            
            if (!isFresh) {
                Log.w(TAG, "Skipping arming due to TTL violation: " + slotId);
                return false;
            }
            
            Log.d(TAG, "Content is fresh, proceeding with arming: " + slotId);
            return true;
            
        } catch (Exception e) {
            Log.e(TAG, "Error validating freshness before arming", e);
            return false;
        }
    }
    
    /**
     * Get TTL seconds from configuration
     * 
     * @return TTL in seconds
     */
    private long getTTLSeconds() {
        try {
            return getTTLFromSharedPreferences();
        } catch (Exception e) {
            Log.e(TAG, "Error getting TTL seconds", e);
            return DEFAULT_TTL_SECONDS;
        }
    }
    
    /**
     * Get TTL from SQLite database
     * 
     * @return TTL in seconds
     */
    private long getTTLFromSQLite() { return DEFAULT_TTL_SECONDS; }
    
    /**
     * Get TTL from SharedPreferences
     * 
     * @return TTL in seconds
     */
    private long getTTLFromSharedPreferences() {
        try {
            SharedPreferences prefs = context.getSharedPreferences("DailyNotificationPrefs", Context.MODE_PRIVATE);
            long ttlSeconds = prefs.getLong("ttlSeconds", DEFAULT_TTL_SECONDS);
            
            // Validate TTL range
            ttlSeconds = Math.max(MIN_TTL_SECONDS, Math.min(MAX_TTL_SECONDS, ttlSeconds));
            
            return ttlSeconds;
            
        } catch (Exception e) {
            Log.e(TAG, "Error getting TTL from SharedPreferences", e);
            return DEFAULT_TTL_SECONDS;
        }
    }
    
    /**
     * Get fetchedAt timestamp for a slot
     * 
     * @param slotId Notification slot ID
     * @return FetchedAt timestamp in milliseconds
     */
    private long getFetchedAt(String slotId) {
        try {
            return getFetchedAtFromSharedPreferences(slotId);
        } catch (Exception e) {
            Log.e(TAG, "Error getting fetchedAt for slot: " + slotId, e);
            return 0;
        }
    }
    
    /**
     * Get fetchedAt from SQLite database
     * 
     * @param slotId Notification slot ID
     * @return FetchedAt timestamp in milliseconds
     */
    private long getFetchedAtFromSQLite(String slotId) { return 0; }
    
    /**
     * Get fetchedAt from SharedPreferences
     * 
     * @param slotId Notification slot ID
     * @return FetchedAt timestamp in milliseconds
     */
    private long getFetchedAtFromSharedPreferences(String slotId) {
        try {
            SharedPreferences prefs = context.getSharedPreferences("DailyNotificationPrefs", Context.MODE_PRIVATE);
            return prefs.getLong("last_fetch_" + slotId, 0);
            
        } catch (Exception e) {
            Log.e(TAG, "Error getting fetchedAt from SharedPreferences", e);
            return 0;
        }
    }
    
    /**
     * 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 void logTTLViolation(String slotId, long scheduledTime, long fetchedAt, 
                                long ageAtFireSeconds, long ttlSeconds) {
        try {
            String violationMessage = String.format(
                "TTL violation: slot=%s, scheduled=%d, fetched=%d, age=%ds, ttl=%ds",
                slotId, scheduledTime, fetchedAt, ageAtFireSeconds, ttlSeconds
            );
            
            Log.w(TAG, LOG_CODE_TTL_VIOLATION + ": " + violationMessage);
            
            // Store violation in database or SharedPreferences for analytics
            storeTTLViolation(slotId, scheduledTime, fetchedAt, ageAtFireSeconds, ttlSeconds);
            
        } catch (Exception e) {
            Log.e(TAG, "Error logging TTL violation", e);
        }
    }
    
    /**
     * Store TTL violation for analytics
     */
    private void storeTTLViolation(String slotId, long scheduledTime, long fetchedAt, 
                                  long ageAtFireSeconds, long ttlSeconds) {
        try {
            storeTTLViolationInSharedPreferences(slotId, scheduledTime, fetchedAt, ageAtFireSeconds, ttlSeconds);
        } catch (Exception e) {
            Log.e(TAG, "Error storing TTL violation", e);
        }
    }
    
    /**
     * Store TTL violation in SQLite database
     */
    private void storeTTLViolationInSQLite(String slotId, long scheduledTime, long fetchedAt, 
                                          long ageAtFireSeconds, long ttlSeconds) { }
    
    /**
     * Store TTL violation in SharedPreferences
     */
    private void storeTTLViolationInSharedPreferences(String slotId, long scheduledTime, long fetchedAt, 
                                                     long ageAtFireSeconds, long ttlSeconds) {
        try {
            SharedPreferences prefs = context.getSharedPreferences("DailyNotificationPrefs", Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = prefs.edit();
            
            String violationKey = "ttl_violation_" + slotId + "_" + scheduledTime;
            String violationValue = String.format("%d,%d,%d,%d", fetchedAt, ageAtFireSeconds, ttlSeconds, System.currentTimeMillis());
            
            editor.putString(violationKey, violationValue);
            editor.apply();
            
        } catch (Exception e) {
            Log.e(TAG, "Error storing TTL violation in SharedPreferences", e);
        }
    }
    
    /**
     * Get TTL violation statistics
     * 
     * @return Statistics string
     */
    public String getTTLViolationStats() {
        try {
            return getTTLViolationStatsFromSharedPreferences();
        } catch (Exception e) {
            Log.e(TAG, "Error getting TTL violation stats", e);
            return "Error retrieving TTL violation statistics";
        }
    }
    
    /**
     * Get TTL violation statistics from SQLite
     */
    private String getTTLViolationStatsFromSQLite() { return "TTL violations: 0"; }
    
    /**
     * Get TTL violation statistics from SharedPreferences
     */
    private String getTTLViolationStatsFromSharedPreferences() {
        try {
            SharedPreferences prefs = context.getSharedPreferences("DailyNotificationPrefs", Context.MODE_PRIVATE);
            java.util.Map<String, ?> allPrefs = prefs.getAll();
            
            int violationCount = 0;
            for (String key : allPrefs.keySet()) {
                if (key.startsWith("ttl_violation_")) {
                    violationCount++;
                }
            }
            
            return String.format("TTL violations: %d", violationCount);
            
        } catch (Exception e) {
            Log.e(TAG, "Error getting TTL violation stats from SharedPreferences", e);
            return "Error retrieving TTL violation statistics";
        }
    }
}
