/**
 * NotificationStatusChecker.java
 * 
 * Comprehensive status checking for notification system
 * Provides unified API for UI guidance and troubleshooting
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

package org.timesafari.dailynotification;

import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import androidx.core.app.NotificationManagerCompat;

import com.getcapacitor.JSObject;

/**
 * Comprehensive status checker for notification system
 * 
 * This class provides a unified API to check all aspects of the notification
 * system status, enabling the UI to guide users when notifications don't appear.
 */
public class NotificationStatusChecker {
    
    private static final String TAG = "NotificationStatusChecker";
    
    private final Context context;
    private final NotificationManager notificationManager;
    private final ChannelManager channelManager;
    private final PendingIntentManager pendingIntentManager;
    
    public NotificationStatusChecker(Context context) {
        this.context = context.getApplicationContext();
        this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        this.channelManager = new ChannelManager(context);
        this.pendingIntentManager = new PendingIntentManager(context);
    }
    
    /**
     * Get comprehensive notification system status
     * 
     * @return JSObject containing all status information
     */
    public JSObject getComprehensiveStatus() {
        try {
            Log.d(TAG, "DN|STATUS_CHECK_START");
            
            JSObject status = new JSObject();
            
            // Core permissions
            boolean postNotificationsGranted = checkPostNotificationsPermission();
            boolean exactAlarmsGranted = checkExactAlarmsPermission();
            boolean notificationsEnabledAtOsLevel = checkNotificationsEnabledAtOsLevel();
            
            // Channel status
            boolean channelEnabled = channelManager.isChannelEnabled();
            int channelImportance = channelManager.getChannelImportance();
            String channelId = channelManager.getDefaultChannelId();
            
            // Alarm manager status
            PendingIntentManager.AlarmStatus alarmStatus = pendingIntentManager.getAlarmStatus();
            
            // Overall readiness - all requirements must be met
            boolean canScheduleNow = postNotificationsGranted && 
                                   channelEnabled && 
                                   exactAlarmsGranted &&
                                   notificationsEnabledAtOsLevel;
            
            // Build status object
            status.put("postNotificationsGranted", postNotificationsGranted);
            status.put("exactAlarmsGranted", exactAlarmsGranted);
            status.put("notificationsEnabledAtOsLevel", notificationsEnabledAtOsLevel);
            status.put("channelEnabled", channelEnabled);
            status.put("channelImportance", channelImportance);
            status.put("channelId", channelId);
            status.put("canScheduleNow", canScheduleNow);
            status.put("exactAlarmsSupported", alarmStatus.exactAlarmsSupported);
            status.put("androidVersion", alarmStatus.androidVersion);
            
            // Add issue descriptions for UI guidance
            JSObject issues = new JSObject();
            if (!postNotificationsGranted) {
                issues.put("postNotifications", "POST_NOTIFICATIONS permission not granted");
            }
            if (!notificationsEnabledAtOsLevel) {
                issues.put("osNotificationsDisabled", "Notifications disabled at OS level");
            }
            if (!channelEnabled) {
                issues.put("channelDisabled", "Notification channel is disabled or blocked");
            }
            if (!exactAlarmsGranted) {
                issues.put("exactAlarms", "Exact alarm permission not granted");
            }
            status.put("issues", issues);
            
            // Add actionable guidance
            JSObject guidance = new JSObject();
            if (!postNotificationsGranted) {
                guidance.put("postNotifications", "Request notification permission in app settings");
            }
            if (!notificationsEnabledAtOsLevel) {
                guidance.put("osNotificationsDisabled", "Enable notifications in system settings");
            }
            if (!channelEnabled) {
                guidance.put("channelDisabled", "Enable notifications in system settings");
            }
            if (!exactAlarmsGranted) {
                guidance.put("exactAlarms", "Grant exact alarm permission in system settings");
            }
            status.put("guidance", guidance);
            
            Log.d(TAG, "DN|STATUS_CHECK_OK canSchedule=" + canScheduleNow + 
                      " postGranted=" + postNotificationsGranted + 
                      " channelEnabled=" + channelEnabled + 
                      " exactGranted=" + exactAlarmsGranted);
            
            return status;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|STATUS_CHECK_ERR err=" + e.getMessage(), e);
            
            // Return minimal status on error
            JSObject errorStatus = new JSObject();
            errorStatus.put("canScheduleNow", false);
            errorStatus.put("error", e.getMessage());
            return errorStatus;
        }
    }
    
    /**
     * Check POST_NOTIFICATIONS permission status
     * Always checks OS-level notification enablement for all API levels
     * 
     * @return true if permission is granted AND notifications enabled at OS level, false otherwise
     */
    private boolean checkPostNotificationsPermission() {
        try {
            boolean permissionGranted = false;
            boolean osLevelEnabled = false;
            
            // Check POST_NOTIFICATIONS permission (Android 13+)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                permissionGranted = context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) 
                       == PackageManager.PERMISSION_GRANTED;
            } else {
                // Pre-Android 13: permission granted at install time
                permissionGranted = true;
            }
            
            // Always check OS-level notification enablement (critical for all API levels)
            osLevelEnabled = NotificationManagerCompat.from(context).areNotificationsEnabled();
            
            // Both must be true
            boolean result = permissionGranted && osLevelEnabled;
            
            if (!osLevelEnabled && permissionGranted) {
                Log.w(TAG, "DN|PERM_CHECK_WARN Permission granted but OS-level notifications disabled");
            }
            
            return result;
        } catch (Exception e) {
            Log.e(TAG, "DN|PERM_CHECK_ERR postNotifications err=" + e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * Check if notifications are enabled at OS level
     * Separate check from permission check - users can disable at OS level even with permission
     * 
     * @return true if notifications enabled at OS level, false otherwise
     */
    private boolean checkNotificationsEnabledAtOsLevel() {
        try {
            return NotificationManagerCompat.from(context).areNotificationsEnabled();
        } catch (Exception e) {
            Log.e(TAG, "DN|OS_CHECK_ERR err=" + e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * Check SCHEDULE_EXACT_ALARM permission status
     * 
     * @return true if permission is granted, false otherwise
     */
    private boolean checkExactAlarmsPermission() {
        try {
            return pendingIntentManager.canScheduleExactAlarms();
        } catch (Exception e) {
            Log.e(TAG, "DN|PERM_CHECK_ERR exactAlarms err=" + e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * Get detailed channel status information
     * 
     * @return JSObject containing channel details
     */
    public JSObject getChannelStatus() {
        try {
            Log.d(TAG, "DN|CHANNEL_STATUS_START");
            
            JSObject channelStatus = new JSObject();
            
            boolean channelExists = channelManager.ensureChannelExists();
            boolean channelEnabled = channelManager.isChannelEnabled();
            int channelImportance = channelManager.getChannelImportance();
            String channelId = channelManager.getDefaultChannelId();
            
            channelStatus.put("channelExists", channelExists);
            channelStatus.put("channelEnabled", channelEnabled);
            channelStatus.put("channelImportance", channelImportance);
            channelStatus.put("channelId", channelId);
            channelStatus.put("channelBlocked", channelImportance == NotificationManager.IMPORTANCE_NONE);
            
            // Add importance description
            String importanceDescription = getImportanceDescription(channelImportance);
            channelStatus.put("importanceDescription", importanceDescription);
            
            Log.d(TAG, "DN|CHANNEL_STATUS_OK enabled=" + channelEnabled + 
                      " importance=" + channelImportance + 
                      " blocked=" + (channelImportance == NotificationManager.IMPORTANCE_NONE));
            
            return channelStatus;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|CHANNEL_STATUS_ERR err=" + e.getMessage(), e);
            
            JSObject errorStatus = new JSObject();
            errorStatus.put("error", e.getMessage());
            return errorStatus;
        }
    }
    
    /**
     * Get alarm manager status information
     * 
     * @return JSObject containing alarm manager details
     */
    public JSObject getAlarmStatus() {
        try {
            Log.d(TAG, "DN|ALARM_STATUS_START");
            
            PendingIntentManager.AlarmStatus alarmStatus = pendingIntentManager.getAlarmStatus();
            
            JSObject status = new JSObject();
            status.put("exactAlarmsSupported", alarmStatus.exactAlarmsSupported);
            status.put("exactAlarmsGranted", alarmStatus.exactAlarmsGranted);
            status.put("androidVersion", alarmStatus.androidVersion);
            status.put("canScheduleExactAlarms", alarmStatus.exactAlarmsGranted);
            
            Log.d(TAG, "DN|ALARM_STATUS_OK supported=" + alarmStatus.exactAlarmsSupported + 
                      " granted=" + alarmStatus.exactAlarmsGranted + 
                      " android=" + alarmStatus.androidVersion);
            
            return status;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|ALARM_STATUS_ERR err=" + e.getMessage(), e);
            
            JSObject errorStatus = new JSObject();
            errorStatus.put("error", e.getMessage());
            return errorStatus;
        }
    }
    
    /**
     * Get permission status information
     * 
     * @return JSObject containing permission details
     */
    public JSObject getPermissionStatus() {
        try {
            Log.d(TAG, "DN|PERMISSION_STATUS_START");
            
            JSObject permissionStatus = new JSObject();
            
            boolean postNotificationsGranted = checkPostNotificationsPermission();
            boolean exactAlarmsGranted = checkExactAlarmsPermission();
            
            permissionStatus.put("postNotificationsGranted", postNotificationsGranted);
            permissionStatus.put("exactAlarmsGranted", exactAlarmsGranted);
            permissionStatus.put("allPermissionsGranted", postNotificationsGranted && exactAlarmsGranted);
            
            // Add permission descriptions
            JSObject descriptions = new JSObject();
            descriptions.put("postNotifications", "Allows app to display notifications");
            descriptions.put("exactAlarms", "Allows app to schedule precise alarm times");
            permissionStatus.put("descriptions", descriptions);
            
            Log.d(TAG, "DN|PERMISSION_STATUS_OK postGranted=" + postNotificationsGranted + 
                      " exactGranted=" + exactAlarmsGranted);
            
            return permissionStatus;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|PERMISSION_STATUS_ERR err=" + e.getMessage(), e);
            
            JSObject errorStatus = new JSObject();
            errorStatus.put("error", e.getMessage());
            return errorStatus;
        }
    }
    
    /**
     * Get human-readable importance description
     * 
     * @param importance Notification importance level
     * @return Human-readable description
     */
    private String getImportanceDescription(int importance) {
        switch (importance) {
            case NotificationManager.IMPORTANCE_NONE:
                return "Blocked - No notifications will be shown";
            case NotificationManager.IMPORTANCE_MIN:
                return "Minimal - Only shown in notification shade";
            case NotificationManager.IMPORTANCE_LOW:
                return "Low - Shown in notification shade, no sound";
            case NotificationManager.IMPORTANCE_DEFAULT:
                return "Default - Shown with sound and on lock screen";
            case NotificationManager.IMPORTANCE_HIGH:
                return "High - Shown with sound, on lock screen, and heads-up";
            case NotificationManager.IMPORTANCE_MAX:
                return "Maximum - Shown with sound, on lock screen, heads-up, and can bypass Do Not Disturb";
            default:
                return "Unknown importance level: " + importance;
        }
    }
    
    /**
     * Check if the notification system is ready to schedule notifications
     * Includes OS-level notification enablement check
     * 
     * @return true if ready, false otherwise
     */
    public boolean isReadyToSchedule() {
        try {
            boolean postNotificationsGranted = checkPostNotificationsPermission();
            boolean notificationsEnabledAtOsLevel = checkNotificationsEnabledAtOsLevel();
            boolean channelEnabled = channelManager.isChannelEnabled();
            boolean exactAlarmsGranted = checkExactAlarmsPermission();
            
            boolean ready = postNotificationsGranted && 
                           notificationsEnabledAtOsLevel &&
                           channelEnabled && 
                           exactAlarmsGranted;
            
            Log.d(TAG, "DN|READY_CHECK ready=" + ready + 
                      " postGranted=" + postNotificationsGranted + 
                      " osEnabled=" + notificationsEnabledAtOsLevel +
                      " channelEnabled=" + channelEnabled + 
                      " exactGranted=" + exactAlarmsGranted);
            
            return ready;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|READY_CHECK_ERR err=" + e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * Get comprehensive readiness report with issue codes and fix actions
     * 
     * Returns a structured report with:
     * - Individual requirement booleans
     * - List of issues with stable codes, human messages, and fix actions
     * - Deep link suggestions for fixing issues
     * 
     * @return JSObject containing readiness report
     */
    public JSObject getReadinessReport() {
        try {
            Log.d(TAG, "DN|READINESS_REPORT_START");
            
            JSObject report = new JSObject();
            
            // Check all requirements
            boolean postNotificationsGranted = false;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                postNotificationsGranted = context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) 
                       == PackageManager.PERMISSION_GRANTED;
            } else {
                postNotificationsGranted = true; // Pre-Android 13: granted at install
            }
            
            boolean notificationsEnabledAtOsLevel = checkNotificationsEnabledAtOsLevel();
            boolean channelEnabled = channelManager.isChannelEnabled();
            boolean exactAlarmsGranted = checkExactAlarmsPermission();
            
            // Overall readiness
            boolean canScheduleNow = postNotificationsGranted && 
                                   notificationsEnabledAtOsLevel &&
                                   channelEnabled && 
                                   exactAlarmsGranted;
            
            // Build requirements object
            JSObject requirements = new JSObject();
            requirements.put("postNotificationsGranted", postNotificationsGranted);
            requirements.put("notificationsEnabledAtOsLevel", notificationsEnabledAtOsLevel);
            requirements.put("channelEnabled", channelEnabled);
            requirements.put("exactAlarmsGranted", exactAlarmsGranted);
            requirements.put("canScheduleNow", canScheduleNow);
            
            report.put("requirements", requirements);
            
            // Build issues array with codes, messages, and fix actions
            com.getcapacitor.JSArray issuesArray = new com.getcapacitor.JSArray();
            
            if (!postNotificationsGranted && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                JSObject issue = new JSObject();
                issue.put("code", "POST_NOTIFICATIONS_DENIED");
                issue.put("humanMessage", "Notification permission not granted");
                issue.put("fixAction", "Request notification permission in app settings");
                issue.put("deepLink", "app://settings/notifications");
                issuesArray.put(issue);
            }
            
            if (!notificationsEnabledAtOsLevel) {
                JSObject issue = new JSObject();
                issue.put("code", "OS_NOTIFICATIONS_DISABLED");
                issue.put("humanMessage", "Notifications disabled at system level");
                issue.put("fixAction", "Enable notifications in system settings");
                issue.put("deepLink", "android.settings.ACTION_APP_NOTIFICATION_SETTINGS");
                issuesArray.put(issue);
            }
            
            if (!channelEnabled) {
                JSObject issue = new JSObject();
                issue.put("code", "CHANNEL_DISABLED");
                issue.put("humanMessage", "Notification channel is disabled or blocked");
                issue.put("fixAction", "Enable notification channel in system settings");
                issue.put("deepLink", "android.settings.CHANNEL_NOTIFICATION_SETTINGS");
                issuesArray.put(issue);
            }
            
            if (!exactAlarmsGranted && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                JSObject issue = new JSObject();
                issue.put("code", "EXACT_ALARMS_DENIED");
                issue.put("humanMessage", "Exact alarm permission not granted");
                issue.put("fixAction", "Grant 'Alarms & reminders' permission in system settings");
                issue.put("deepLink", "android.settings.REQUEST_SCHEDULE_EXACT_ALARM");
                issuesArray.put(issue);
            }
            
            report.put("issues", issuesArray);
            report.put("issueCount", issuesArray.length());
            report.put("canScheduleNow", canScheduleNow);
            
            Log.d(TAG, "DN|READINESS_REPORT_OK canSchedule=" + canScheduleNow + 
                      " issues=" + issuesArray.length());
            
            return report;
            
        } catch (Exception e) {
            Log.e(TAG, "DN|READINESS_REPORT_ERR err=" + e.getMessage(), e);
            
            JSObject errorReport = new JSObject();
            errorReport.put("canScheduleNow", false);
            errorReport.put("error", e.getMessage());
            errorReport.put("issues", new com.getcapacitor.JSArray());
            return errorReport;
        }
    }
    
    /**
     * Get a summary of issues preventing notification scheduling
     * Includes OS-level notification enablement check
     * 
     * @return Array of issue descriptions
     */
    public String[] getIssues() {
        try {
            java.util.List<String> issues = new java.util.ArrayList<>();
            
            if (!checkPostNotificationsPermission()) {
                issues.add("POST_NOTIFICATIONS permission not granted");
            }
            
            if (!checkNotificationsEnabledAtOsLevel()) {
                issues.add("Notifications disabled at OS level");
            }
            
            if (!channelManager.isChannelEnabled()) {
                issues.add("Notification channel is disabled or blocked");
            }
            
            if (!checkExactAlarmsPermission()) {
                issues.add("Exact alarm permission not granted");
            }
            
            return issues.toArray(new String[0]);
            
        } catch (Exception e) {
            Log.e(TAG, "DN|ISSUES_ERR err=" + e.getMessage(), e);
            return new String[]{"Error checking status: " + e.getMessage()};
        }
    }
    
    /**
     * Get notification status information (schedules and history)
     * 
     * This method delegates to a Kotlin helper function that handles the async
     * database operations. The helper is defined in DailyNotificationPlugin.kt
     * as a suspend function, so this Java method uses runBlocking to call it.
     * 
     * Note: This method should typically be called from Kotlin code within a
     * coroutine scope. The plugin method handles the coroutine context.
     * 
     * @param database Database instance for querying schedules and history
     * @return JSObject containing notification status (schedules, last notification time, etc.)
     */
    public JSObject getNotificationStatus(org.timesafari.dailynotification.DailyNotificationDatabase database) {
        try {
            Log.d(TAG, "DN|NOTIFICATION_STATUS_START");
            
            // Delegate to Kotlin helper function (uses runBlocking internally)
            // This is safe because status checks are quick operations
            return org.timesafari.dailynotification.NotificationStatusHelper.getNotificationStatusBlocking(database);
            
        } catch (Exception e) {
            Log.e(TAG, "DN|NOTIFICATION_STATUS_ERR err=" + e.getMessage(), e);
            
            JSObject errorStatus = new JSObject();
            errorStatus.put("error", e.getMessage());
            errorStatus.put("isEnabled", false);
            errorStatus.put("isScheduled", false);
            errorStatus.put("scheduledCount", 0);
            return errorStatus;
        }
    }
}
