package org.timesafari.dailynotification;

import static org.junit.Assert.*;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Calendar;

/**
 * Instrumentation tests for Daily Notification Plugin
 * 
 * Tests critical notification scheduling and delivery paths
 */
@RunWith(AndroidJUnit4.class)
public class NotificationInstrumentationTest {

    private Context appContext;
    private AlarmManager alarmManager;

    @Rule
    public GrantPermissionRule permissionRule = GrantPermissionRule.grant(
        android.Manifest.permission.POST_NOTIFICATIONS,
        android.Manifest.permission.SCHEDULE_EXACT_ALARM
    );

    @Before
    public void setUp() {
        appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        alarmManager = (AlarmManager) appContext.getSystemService(Context.ALARM_SERVICE);
    }

    @Test
    public void testNotifyReceiverRegistration() {
        // Verify NotifyReceiver is registered in AndroidManifest
        Intent intent = new Intent(appContext, NotifyReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
            appContext,
            0,
            intent,
            PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE
        );
        
        // If NotifyReceiver is registered, we can create a PendingIntent for it
        assertNotNull("NotifyReceiver should be registered in AndroidManifest", pendingIntent);
    }

    @Test
    public void testAlarmScheduling() {
        // Test that alarms can be scheduled
        long triggerTime = System.currentTimeMillis() + 60000; // 1 minute from now
        
        Intent intent = new Intent(appContext, NotifyReceiver.class);
        intent.putExtra("title", "Test Notification");
        intent.putExtra("body", "Test body");
        
        int requestCode = NotifyReceiver.Companion.getRequestCode(triggerTime);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
            appContext,
            requestCode,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        try {
            // Use setAlarmClock for Android 5.0+
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(
                    triggerTime,
                    null
                );
                alarmManager.setAlarmClock(alarmClockInfo, pendingIntent);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(
                    AlarmManager.RTC_WAKEUP,
                    triggerTime,
                    pendingIntent
                );
            } else {
                alarmManager.setExact(
                    AlarmManager.RTC_WAKEUP,
                    triggerTime,
                    pendingIntent
                );
            }

            // Verify alarm is scheduled
            boolean isScheduled = NotifyReceiver.Companion.isAlarmScheduled(appContext, triggerTime);
            assertTrue("Alarm should be scheduled", isScheduled);

            // Clean up
            alarmManager.cancel(pendingIntent);
        } catch (SecurityException e) {
            fail("Should have permission to schedule exact alarms: " + e.getMessage());
        }
    }

    @Test
    public void testUniqueRequestCodes() {
        // Test that different trigger times generate different request codes
        long triggerTime1 = System.currentTimeMillis() + 60000;
        long triggerTime2 = System.currentTimeMillis() + 120000;
        
        int requestCode1 = NotifyReceiver.Companion.getRequestCode(triggerTime1);
        int requestCode2 = NotifyReceiver.Companion.getRequestCode(triggerTime2);
        
        // Request codes should be different for different trigger times
        assertNotEquals("Different trigger times should generate different request codes", 
            requestCode1, requestCode2);
    }

    @Test
    public void testAlarmStatusCheck() {
        // Test isAlarmScheduled method
        long triggerTime = System.currentTimeMillis() + 60000;
        
        // Initially should not be scheduled
        boolean initiallyScheduled = NotifyReceiver.Companion.isAlarmScheduled(appContext, triggerTime);
        assertFalse("Alarm should not be scheduled initially", initiallyScheduled);

        // Schedule alarm
        Intent intent = new Intent(appContext, NotifyReceiver.class);
        intent.putExtra("title", "Test");
        intent.putExtra("body", "Test");
        
        int requestCode = NotifyReceiver.Companion.getRequestCode(triggerTime);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
            appContext,
            requestCode,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(
                    triggerTime,
                    null
                );
                alarmManager.setAlarmClock(alarmClockInfo, pendingIntent);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(
                    AlarmManager.RTC_WAKEUP,
                    triggerTime,
                    pendingIntent
                );
            } else {
                alarmManager.setExact(
                    AlarmManager.RTC_WAKEUP,
                    triggerTime,
                    pendingIntent
                );
            }

            // Now should be scheduled
            boolean afterScheduling = NotifyReceiver.Companion.isAlarmScheduled(appContext, triggerTime);
            assertTrue("Alarm should be scheduled after calling setAlarmClock", afterScheduling);

            // Clean up
            alarmManager.cancel(pendingIntent);
        } catch (SecurityException e) {
            fail("Should have permission to schedule exact alarms: " + e.getMessage());
        }
    }

    @Test
    public void testNextAlarmTime() {
        // Test getNextAlarmTime method (requires Android 5.0+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Long nextAlarmTime = NotifyReceiver.Companion.getNextAlarmTime(appContext);
            
            // May be null if no alarm scheduled, which is valid
            if (nextAlarmTime != null) {
                assertTrue("Next alarm time should be in the future", 
                    nextAlarmTime > System.currentTimeMillis());
            }
        }
    }

    @Test
    public void testAlarmCancellation() {
        // Test that alarms can be cancelled
        long triggerTime = System.currentTimeMillis() + 60000;
        
        Intent intent = new Intent(appContext, NotifyReceiver.class);
        intent.putExtra("title", "Test");
        intent.putExtra("body", "Test");
        
        int requestCode = NotifyReceiver.Companion.getRequestCode(triggerTime);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
            appContext,
            requestCode,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        try {
            // Schedule alarm
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(
                    triggerTime,
                    null
                );
                alarmManager.setAlarmClock(alarmClockInfo, pendingIntent);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(
                    AlarmManager.RTC_WAKEUP,
                    triggerTime,
                    pendingIntent
                );
            } else {
                alarmManager.setExact(
                    AlarmManager.RTC_WAKEUP,
                    triggerTime,
                    pendingIntent
                );
            }

            // Verify scheduled
            assertTrue("Alarm should be scheduled", 
                NotifyReceiver.Companion.isAlarmScheduled(appContext, triggerTime));

            // Cancel alarm
            NotifyReceiver.Companion.cancelNotification(appContext, triggerTime);

            // Verify cancelled
            assertFalse("Alarm should be cancelled", 
                NotifyReceiver.Companion.isAlarmScheduled(appContext, triggerTime));
        } catch (SecurityException e) {
            fail("Should have permission to schedule exact alarms: " + e.getMessage());
        }
    }

    @Test
    public void testPendingIntentUniqueness() {
        // Test that PendingIntents with different request codes don't conflict
        long triggerTime1 = System.currentTimeMillis() + 60000;
        long triggerTime2 = System.currentTimeMillis() + 120000;
        
        Intent intent1 = new Intent(appContext, NotifyReceiver.class);
        intent1.putExtra("title", "Test 1");
        intent1.putExtra("body", "Test 1");
        
        Intent intent2 = new Intent(appContext, NotifyReceiver.class);
        intent2.putExtra("title", "Test 2");
        intent2.putExtra("body", "Test 2");
        
        int requestCode1 = NotifyReceiver.Companion.getRequestCode(triggerTime1);
        int requestCode2 = NotifyReceiver.Companion.getRequestCode(triggerTime2);
        
        PendingIntent pendingIntent1 = PendingIntent.getBroadcast(
            appContext,
            requestCode1,
            intent1,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );
        
        PendingIntent pendingIntent2 = PendingIntent.getBroadcast(
            appContext,
            requestCode2,
            intent2,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        // Both PendingIntents should be created successfully
        assertNotNull("First PendingIntent should be created", pendingIntent1);
        assertNotNull("Second PendingIntent should be created", pendingIntent2);
        
        // They should be different objects
        assertNotSame("PendingIntents should be different objects", 
            pendingIntent1, pendingIntent2);
    }
}

