<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use App\Jobs\SendSiteAccessLinkJob;
use App\Mail\SiteAccessLinkMail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Mail;
use Mtc\MercuryDataModels\SiteAccessRequest;
use Tests\TenantTestCase;

class SendSiteAccessLinksTest extends TenantTestCase
{
    #[\Override]
    protected function setUp(): void
    {
        parent::setUp();

        Settings::make([
            'tab' => 'Site',
            'section' => 'General',
            'group' => 'Restricted access',
            'name' => 'Enabled',
            'config_key' => 'site-access-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Site',
            'section' => 'General',
            'group' => 'Restricted access',
            'name' => 'Access mode',
            'config_key' => 'site-access-mode',
            'type' => 'string',
            'value' => 'email-request',
        ]);
    }

    public function testCommandDispatchesJobForPendingRequest(): void
    {
        Bus::fake();

        SiteAccessRequest::create(['email' => 'foo@mtc.co.uk']);

        $this->artisan('site-access:send-pending-links')->assertExitCode(0);

        Bus::assertDispatched(SendSiteAccessLinkJob::class, 1);
    }

    public function testCommandSkipsRequestsAlreadySent(): void
    {
        Bus::fake();

        SiteAccessRequest::create([
            'email' => 'foo@mtc.co.uk',
            'sent_at' => Carbon::now(),
        ]);

        $this->artisan('site-access:send-pending-links')->assertExitCode(0);

        Bus::assertNotDispatched(SendSiteAccessLinkJob::class);
    }

    public function testCommandSkipsExpiredRequests(): void
    {
        Bus::fake();

        SiteAccessRequest::create([
            'email' => 'foo@mtc.co.uk',
            'expires_at' => Carbon::now()->subMinute(),
        ]);

        $this->artisan('site-access:send-pending-links')->assertExitCode(0);

        Bus::assertNotDispatched(SendSiteAccessLinkJob::class);
    }

    public function testCommandNoopsWhenModeIsPassword(): void
    {
        Bus::fake();
        Settings::update('site-access-mode', 'password');

        SiteAccessRequest::create(['email' => 'foo@mtc.co.uk']);

        $this->artisan('site-access:send-pending-links')->assertExitCode(0);

        Bus::assertNotDispatched(SendSiteAccessLinkJob::class);
    }

    public function testCommandNoopsWhenRestrictedAccessDisabled(): void
    {
        Bus::fake();
        Settings::update('site-access-enabled', false);

        SiteAccessRequest::create(['email' => 'foo@mtc.co.uk']);

        $this->artisan('site-access:send-pending-links')->assertExitCode(0);

        Bus::assertNotDispatched(SendSiteAccessLinkJob::class);
    }

    public function testJobSendsMailAndMarksRequestSent(): void
    {
        Mail::fake();

        $request = SiteAccessRequest::create(['email' => 'foo@mtc.co.uk']);

        new SendSiteAccessLinkJob($request->id)->handle();

        Mail::assertQueued(
            SiteAccessLinkMail::class,
            fn (SiteAccessLinkMail $mail) => $mail->hasTo('foo@mtc.co.uk'),
        );

        $this->assertNotNull($request->fresh()->sent_at);
    }

    public function testJobSkipsIfAlreadySent(): void
    {
        Mail::fake();

        $request = SiteAccessRequest::create([
            'email' => 'foo@mtc.co.uk',
            'sent_at' => Carbon::now()->subMinute(),
        ]);

        new SendSiteAccessLinkJob($request->id)->handle();

        Mail::assertNothingQueued();
        Mail::assertNothingSent();
    }

    public function testExpiryHonoursConfiguredSetting(): void
    {
        Settings::make([
            'tab' => 'Site',
            'section' => 'General',
            'group' => 'Restricted access',
            'name' => 'Access link expiry',
            'config_key' => 'site-access-link-expiry-hours',
            'type' => 'string',
            'value' => '12',
        ]);

        $now = Carbon::create(2026, 5, 12, 12, 0, 0);
        Carbon::setTestNow($now);

        try {
            $request = SiteAccessRequest::create(['email' => 'foo@mtc.co.uk']);

            $this->assertTrue($request->expires_at->equalTo($now->copy()->addHours(12)));
        } finally {
            Carbon::setTestNow();
        }
    }

    public function testExpiryFallsBackToOneHourWhenSettingIsInvalid(): void
    {
        Settings::make([
            'tab' => 'Site',
            'section' => 'General',
            'group' => 'Restricted access',
            'name' => 'Access link expiry',
            'config_key' => 'site-access-link-expiry-hours',
            'type' => 'string',
            'value' => '999',
        ]);

        $now = Carbon::create(2026, 5, 12, 12, 0, 0);
        Carbon::setTestNow($now);

        try {
            $request = SiteAccessRequest::create(['email' => 'foo@mtc.co.uk']);

            $this->assertTrue($request->expires_at->equalTo($now->copy()->addHour()));
        } finally {
            Carbon::setTestNow();
        }
    }

    public function testJobSkipsIfExpired(): void
    {
        Mail::fake();

        $request = SiteAccessRequest::create([
            'email' => 'foo@mtc.co.uk',
            'expires_at' => Carbon::now()->subMinute(),
        ]);

        new SendSiteAccessLinkJob($request->id)->handle();

        Mail::assertNothingQueued();
        Mail::assertNothingSent();
        $this->assertNull($request->fresh()->sent_at);
    }
}
