/**
 * DozeFallbackWorker.java
 * 
 * WorkManager worker for handling deep doze fallback scenarios
 * Re-arms exact alarms if they get pruned during deep doze mode
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

package org.timesafari.dailynotification;

import android.app.AlarmManager;
import android.content.Context;
import android.os.Trace;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * WorkManager worker for doze fallback scenarios
 * 
 * This worker runs 30 minutes before scheduled notifications to check
 * if exact alarms are still active and re-arm them if needed.
 */
public class DozeFallbackWorker extends Worker {
    
    private static final String TAG = "DozeFallbackWorker";
    
    public DozeFallbackWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
    
    @NonNull
    @Override
    public Result doWork() {
        Trace.beginSection("DN:DozeFallback");
        try {
            long scheduledTime = getInputData().getLong("scheduled_time", -1);
            String action = getInputData().getString("action");
            
            if (scheduledTime == -1 || !"doze_fallback".equals(action)) {
                Log.e(TAG, "DN|DOZE_FALLBACK_ERR invalid_input_data");
                return Result.failure();
            }
            
            Log.d(TAG, "DN|DOZE_FALLBACK_START scheduled_time=" + scheduledTime);
            
            // Check if we're within 30 minutes of the scheduled time
            long currentTime = System.currentTimeMillis();
            long timeUntilNotification = scheduledTime - currentTime;
            
            if (timeUntilNotification < 0) {
                Log.w(TAG, "DN|DOZE_FALLBACK_SKIP notification_already_past");
                return Result.success();
            }
            
            if (timeUntilNotification > TimeUnit.MINUTES.toMillis(30)) {
                Log.w(TAG, "DN|DOZE_FALLBACK_SKIP too_early time_until=" + (timeUntilNotification / 1000 / 60) + "min");
                return Result.success();
            }
            
            // Check if exact alarm is still scheduled
            boolean alarmStillActive = checkExactAlarmStatus(scheduledTime);
            
            if (!alarmStillActive) {
                Log.w(TAG, "DN|DOZE_FALLBACK_REARM exact_alarm_missing scheduled_time=" + scheduledTime);
                
                // Re-arm the exact alarm
                boolean rearmed = rearmExactAlarm(scheduledTime);
                
                if (rearmed) {
                    Log.i(TAG, "DN|DOZE_FALLBACK_OK exact_alarm_rearmed");
                    return Result.success();
                } else {
                    Log.e(TAG, "DN|DOZE_FALLBACK_ERR rearm_failed");
                    return Result.retry();
                }
            } else {
                Log.d(TAG, "DN|DOZE_FALLBACK_OK exact_alarm_active");
                return Result.success();
            }
            
        } catch (Exception e) {
            Log.e(TAG, "DN|DOZE_FALLBACK_ERR exception=" + e.getMessage(), e);
            return Result.retry();
        } finally {
            Trace.endSection();
        }
    }
    
    /**
     * Check if exact alarm is still active for the scheduled time
     * 
     * @param scheduledTime The scheduled notification time
     * @return true if alarm is still active, false otherwise
     */
    private boolean checkExactAlarmStatus(long scheduledTime) {
        try {
            // Get all notifications from storage
            DailyNotificationStorage storage = new DailyNotificationStorage(getApplicationContext());
            List<NotificationContent> notifications = storage.getAllNotifications();
            
            // Look for notification scheduled at the target time (within 1 minute tolerance)
            long toleranceMs = 60 * 1000; // 1 minute tolerance
            
            for (NotificationContent notification : notifications) {
                if (Math.abs(notification.getScheduledTime() - scheduledTime) <= toleranceMs) {
                    Log.d(TAG, "DN|DOZE_FALLBACK_CHECK found_notification id=" + notification.getId());
                    return true;
                }
            }
            
            Log.w(TAG, "DN|DOZE_FALLBACK_CHECK no_notification_found scheduled_time=" + scheduledTime);
            return false;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|DOZE_FALLBACK_CHECK_ERR err=" + e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * Re-arm the exact alarm for the scheduled time
     * 
     * @param scheduledTime The scheduled notification time
     * @return true if re-arming succeeded, false otherwise
     */
    private boolean rearmExactAlarm(long scheduledTime) {
        try {
            // Get all notifications from storage
            DailyNotificationStorage storage = new DailyNotificationStorage(getApplicationContext());
            List<NotificationContent> notifications = storage.getAllNotifications();
            
            // Find the notification scheduled at the target time
            long toleranceMs = 60 * 1000; // 1 minute tolerance
            
            for (NotificationContent notification : notifications) {
                if (Math.abs(notification.getScheduledTime() - scheduledTime) <= toleranceMs) {
                    Log.d(TAG, "DN|DOZE_FALLBACK_REARM found_target id=" + notification.getId());
                    
                    // Re-schedule the notification
                    DailyNotificationScheduler scheduler = new DailyNotificationScheduler(
                        getApplicationContext(),
                        (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE)
                    );
                    
                    boolean scheduled = scheduler.scheduleNotification(notification);
                    
                    if (scheduled) {
                        Log.i(TAG, "DN|DOZE_FALLBACK_REARM_OK id=" + notification.getId());
                        return true;
                    } else {
                        Log.e(TAG, "DN|DOZE_FALLBACK_REARM_FAIL id=" + notification.getId());
                        return false;
                    }
                }
            }
            
            Log.w(TAG, "DN|DOZE_FALLBACK_REARM_ERR no_target_found scheduled_time=" + scheduledTime);
            return false;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|DOZE_FALLBACK_REARM_ERR exception=" + e.getMessage(), e);
            return false;
        }
    }
}
