<?php

namespace Tests\Feature;

use App\Mail\FailedEnquiryReport;
use App\Models\SystemAlert;
use Carbon\Carbon;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Mail;
use Mtc\Crm\Models\EnquiryAction;
use Tests\DatabaseTestCase;

class CheckFailedEnquiryActionsTest extends DatabaseTestCase
{
    /**
     * @test
     *
     * Assert that the `check:failed-enquiry-actions` command sends an email alert
     * when there are failed enquiry actions. Ensure that the email is sent to the
     * configured developer alert addresses, and that the `last_sent_at` timestamp
     * for the SystemAlert is updated accordingly.
     */
    public function testFailedEnquiryAlertingSendsEmail()
    {
        Mail::fake();

        // Create failed enquiry actions with 'failed' state
        EnquiryAction::factory()->state([
            'processed_at' => null,
            'failed_at' => Carbon::now(),
        ])->count(3)->create();

        // Run the scheduled command
        Artisan::call('check:failed-enquiry-actions');

        // Assert that the email was sent
        Mail::assertSent(FailedEnquiryReport::class, function ($mail) {
            // Get the list of developer alert email addresses
            $developerEmails = config('mail.developer_alerts');

            // Check if the email was sent to any of the addresses in the array
            return collect($developerEmails)->contains(fn ($email) => $mail->hasTo($email));
        });

        // Ensure `last_sent_at` is updated
        $systemAlert = SystemAlert::where('alert_type', 'enquiry_failure')->first();
        $this->assertNotNull($systemAlert->last_sent_at);
    }

    /**
     * @test
     *
     * Assert that the scheduled command does not create a new SystemAlert
     * if it has already been sent within the last day, and also that no
     * additional emails are sent.
     */
    public function testAlertSuppressionDueToFrequencyLimit()
    {
        Mail::fake();

        // Create failed enquiry actions with 'failed' state
        EnquiryAction::factory()->state([
            'processed_at' => null,
            'failed_at' => Carbon::now(),
        ])->count(3)->create();

        // Run the scheduled command
        Artisan::call('check:failed-enquiry-actions');

        // Ensure only one SystemAlert is created
        $this->assertEquals(1, SystemAlert::count());
        $systemAlert = SystemAlert::where('alert_type', 'enquiry_failure')->first();
        $this->assertNotNull($systemAlert);
        $this->assertEquals('enquiry_failure', $systemAlert->alert_type);

        // Capture initial `last_sent_at` time
        $initialSentAt = $systemAlert->last_sent_at;

        // Use Carbon's time travel to advance time
        Carbon::setTestNow(Carbon::now()->addSeconds(2));

        // Call the scheduled command again
        Artisan::call('check:failed-enquiry-actions');

        // Ensure no new alert due to frequency limit
        $this->assertEquals(1, SystemAlert::count());

        // Verify `last_sent_at` remains unchanged
        $systemAlert->refresh();
        $this->assertEquals($initialSentAt, $systemAlert->last_sent_at);

        Carbon::setTestNow();
    }

    /**
     * @test
     *
     * Assert that the `purgeOldFailures` method deletes SystemAlert records older than one month.
     */
    public function testPurgeOldFailuresRemovesOldSystemAlerts()
    {
        // Create older system alerts (older than 1 month)
        SystemAlert::factory()->create([
            'alert_type' => 'enquiry_failure',
            'last_sent_at' => Carbon::now()->subMonths(2),
        ]);
        SystemAlert::factory()->create([
            'alert_type' => 'enquiry_failure',
            'last_sent_at' => Carbon::now()->subMonths(2),
        ]);

        // Create newer system alerts (within the last month)
        SystemAlert::factory()->create([
            'alert_type' => 'enquiry_failure',
            'last_sent_at' => Carbon::now()->subWeeks(2),
        ]);
        SystemAlert::factory()->create([
            'alert_type' => 'enquiry_failure',
            'last_sent_at' => Carbon::now()->subDays(10),
        ]);

        // Assert we have 4 total system alerts prior to purge
        $this->assertEquals(4, SystemAlert::count());

        // Run the command (which includes the purge logic)
        $this->artisan('check:failed-enquiry-actions');

        // Only the two newer records should remain
        $this->assertEquals(2, SystemAlert::count());

        // Confirm the remaining records all have `last_sent_at >= now()->subMonth()`
        SystemAlert::all()->each(function (SystemAlert $alert) {
            $this->assertTrue(
                $alert->last_sent_at->greaterThanOrEqualTo(Carbon::now()->subMonth())
            );
        });
    }
}
