<?php

namespace Tests\Unit\Rules;

use App\Rules\MtcRoleRestriction;
use Illuminate\Support\Facades\Auth;
use Mockery;
use Tests\TestCase;

class MtcRoleRestrictionTest extends TestCase
{
    private function mockAuthUser(bool $hasMtcRole): void
    {
        $user = Mockery::mock();
        $user->shouldReceive('hasRole')
            ->with('mtc')
            ->andReturn($hasMtcRole);
        Auth::shouldReceive('user')->andReturn($user);
    }

    public function testPassesWhenRoleIsNotMtc()
    {
        $rule = new MtcRoleRestriction('anyone@example.com');
        $this->assertTrue($rule->passes('role', 'Administrator'));
    }

    public function testPassesForNonMtcRoleRegardlessOfEmail()
    {
        $rule = new MtcRoleRestriction('user@example.com');
        $this->assertTrue($rule->passes('role', 'Editor'));
    }

    public function testPassesWhenMtcUserAssignsMtcRoleToMtcEmail()
    {
        $this->mockAuthUser(true);
        $rule = new MtcRoleRestriction('user@mtc.co.uk');
        $this->assertTrue($rule->passes('role', 'mtc'));
    }

    public function testFailsWhenNonMtcUserAssignsMtcRole()
    {
        $this->mockAuthUser(false);
        $rule = new MtcRoleRestriction('user@mtc.co.uk');
        $this->assertFalse($rule->passes('role', 'mtc'));
        $this->assertEquals('Only MTC users can assign the MTC role.', $rule->message());
    }

    public function testFailsWhenMtcUserAssignsMtcRoleToNonMtcEmail()
    {
        $this->mockAuthUser(true);
        $rule = new MtcRoleRestriction('user@example.com');
        $this->assertFalse($rule->passes('role', 'mtc'));
        $this->assertEquals(
            'The MTC role can only be assigned to users with an approved email address.',
            $rule->message()
        );
    }

    public function testFailsWhenNonMtcUserAssignsMtcRoleToNonMtcEmail()
    {
        $this->mockAuthUser(false);
        $rule = new MtcRoleRestriction('user@example.com');
        $this->assertFalse($rule->passes('role', 'mtc'));
        // Should fail on the role check first
        $this->assertEquals('Only MTC users can assign the MTC role.', $rule->message());
    }

    public function testIsCaseInsensitiveForRoleName()
    {
        $this->mockAuthUser(true);
        $rule = new MtcRoleRestriction('user@mtc.co.uk');
        $this->assertTrue($rule->passes('role', 'MTC'));
    }

    public function testIsCaseInsensitiveForEmail()
    {
        $this->mockAuthUser(true);
        $rule = new MtcRoleRestriction('User@MTC.CO.UK');
        $this->assertTrue($rule->passes('role', 'mtc'));
    }

    public function testFailsForEmailContainingMtcButNotMtcCoUk()
    {
        $this->mockAuthUser(true);
        $rule = new MtcRoleRestriction('user@mtc-other.com');
        $this->assertFalse($rule->passes('role', 'mtc'));
        $this->assertEquals(
            'The MTC role can only be assigned to users with an approved email address.',
            $rule->message()
        );
    }

    public function testFailsWhenApprovedDomainIsNotTheEmailSuffix()
    {
        $this->mockAuthUser(true);
        $rule = new MtcRoleRestriction('attacker@mtc.co.uk.evil.com');
        $this->assertFalse($rule->passes('role', 'mtc'));
        $this->assertEquals(
            'The MTC role can only be assigned to users with an approved email address.',
            $rule->message()
        );
    }

    public function testPassesWhenEmailMatchesAdditionalConfiguredDomain()
    {
        config(['auth.mtc_allowed_email_domains' => ['mtc.co.uk', 'partner.com']]);
        $this->mockAuthUser(true);

        $rule = new MtcRoleRestriction('user@partner.com');
        $this->assertTrue($rule->passes('role', 'mtc'));
    }

    public function testFailsWhenEmailMatchesNoneOfTheConfiguredDomains()
    {
        config(['auth.mtc_allowed_email_domains' => ['mtc.co.uk', 'partner.com']]);
        $this->mockAuthUser(true);

        $rule = new MtcRoleRestriction('user@example.com');
        $this->assertFalse($rule->passes('role', 'mtc'));
    }
}
