<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use Carbon\Carbon;
use Mtc\MercuryDataModels\SiteAccessRequest;
use Tests\TenantTestCase;

class RestrictedAccessTest extends TenantTestCase
{
    public function testVehicleAction()
    {
        Settings::make([
            'tab' => 'foo',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'site-access-enabled',
            'value' => false,
            'type' => 'boolean',
        ]);

        $response = $this->getJson(route('get-site'));
        $response->assertStatus(200);

        $this->assertArrayHasKey('restricted_access', $response->json());
        $this->assertFalse($response->json('restricted_access'));

        Settings::update('site-access-enabled', true);
        $response = $this->getJson(route('get-site'));
        $this->assertTrue($response->json('restricted_access'));
    }

    public function testCheckAccess()
    {
        Settings::make([
            'tab' => 'foo',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'site-access-password',
            'value' => 'foo-baz-bar',
            'type' => 'secret',
        ]);

        $response = $this
            ->postJson(route('check-site-access'), [
                'password' => 'lorem-ipsum',
            ]);

        $response->assertStatus(403);

        $response = $this
            ->postJson(route('check-site-access'), [
                'password' => 'foo-baz-bar',
            ]);

        $response->assertStatus(200);
    }

    public function testCheckAccessRejectsWhenModeIsEmailRequest()
    {
        $this->seedEmailRequestMode();
        Settings::make([
            'tab' => 'foo',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'site-access-password',
            'value' => 'foo-baz-bar',
            'type' => 'secret',
        ]);

        $this->postJson(route('check-site-access'), ['password' => 'foo-baz-bar'])
            ->assertStatus(403);
    }

    public function testRequestAccessCreatesRowForWhitelistedDomain()
    {
        $this->seedEmailRequestMode();

        $this->postJson(route('request-site-access'), ['email' => 'foo@mtc.co.uk'])
            ->assertStatus(200);

        $this->assertSame(1, SiteAccessRequest::query()->count());
        $request = SiteAccessRequest::query()->first();
        $this->assertSame('foo@mtc.co.uk', $request->email);
        $this->assertNull($request->sent_at);
        $this->assertNull($request->used_at);
        $this->assertNotEmpty($request->token);
    }

    public function testRequestAccessNormalisesEmailCase()
    {
        $this->seedEmailRequestMode();

        $this->postJson(route('request-site-access'), ['email' => '  Foo@MTC.CO.UK '])
            ->assertStatus(200);

        $this->assertSame('foo@mtc.co.uk', SiteAccessRequest::query()->value('email'));
    }

    public function testRequestAccessRejectsNonWhitelistedDomainSilently()
    {
        $this->seedEmailRequestMode();

        $this->postJson(route('request-site-access'), ['email' => 'foo@otherdomain.com'])
            ->assertStatus(200);

        $this->assertSame(0, SiteAccessRequest::query()->count());
    }

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

        $this->postJson(route('request-site-access'), ['email' => 'foo@mtc.co.uk'])
            ->assertStatus(200);

        $this->assertSame(0, SiteAccessRequest::query()->count());
    }

    public function testRequestAccessValidatesEmailFormat()
    {
        $this->seedEmailRequestMode();

        $this->postJson(route('request-site-access'), ['email' => 'not-an-email'])
            ->assertStatus(422);
    }

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

        $this->postJson(route('verify-site-access-token'), ['token' => $request->token])
            ->assertStatus(200)
            ->assertJson(['success' => true]);

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

    public function testVerifyAccessTokenRejectsExpiredToken()
    {
        $request = SiteAccessRequest::create([
            'email' => 'foo@mtc.co.uk',
            'sent_at' => Carbon::now()->subHours(2),
            'expires_at' => Carbon::now()->subMinute(),
        ]);

        $this->postJson(route('verify-site-access-token'), ['token' => $request->token])
            ->assertStatus(410)
            ->assertJson(['success' => false]);

        $this->assertNull($request->fresh()->used_at);
    }

    public function testVerifyAccessTokenAllowsReuseUntilExpiry()
    {
        $firstUse = Carbon::now()->subMinutes(10)->startOfSecond();
        $request = SiteAccessRequest::create([
            'email' => 'foo@mtc.co.uk',
            'sent_at' => Carbon::now()->subMinutes(15),
            'used_at' => $firstUse,
        ]);

        $this->postJson(route('verify-site-access-token'), ['token' => $request->token])
            ->assertStatus(200)
            ->assertJson(['success' => true]);

        $this->assertTrue($firstUse->equalTo($request->fresh()->used_at));
    }

    public function testVerifyAccessTokenRejectsUsedTokenOnceExpired()
    {
        $request = SiteAccessRequest::create([
            'email' => 'foo@mtc.co.uk',
            'sent_at' => Carbon::now()->subHours(3),
            'used_at' => Carbon::now()->subHours(2),
            'expires_at' => Carbon::now()->subMinute(),
        ]);

        $this->postJson(route('verify-site-access-token'), ['token' => $request->token])
            ->assertStatus(410);
    }

    public function testVerifyAccessTokenRejectsMalformedToken()
    {
        $this->postJson(route('verify-site-access-token'), ['token' => 'short'])
            ->assertStatus(410);
    }

    public function testSiteResourceExposesAccessMode()
    {
        $this->seedEmailRequestMode();

        $this->getJson(route('get-site'))
            ->assertStatus(200)
            ->assertJson([
                'restricted_access' => true,
                'restricted_access_mode' => 'email-request',
            ]);
    }

    public function testRequestAccessIsRateLimited()
    {
        $this->seedEmailRequestMode();

        for ($i = 0; $i < 5; $i++) {
            $this->postJson(route('request-site-access'), ['email' => 'foo@mtc.co.uk'])
                ->assertStatus(200);
        }

        $this->postJson(route('request-site-access'), ['email' => 'foo@mtc.co.uk'])
            ->assertStatus(429);
    }

    private function seedEmailRequestMode(): void
    {
        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',
        ]);

        Settings::make([
            'tab' => 'Site',
            'section' => 'General',
            'group' => 'Restricted access',
            'name' => 'Whitelisted email domains',
            'config_key' => 'site-access-whitelisted-domains',
            'type' => 'text_area',
            'value' => 'mtc.co.uk, partner.com',
        ]);
    }
}
