<?php

namespace Tests\Unit;

use App\Exceptions\RoleNotFound;
use App\Master\InviteManager;
use App\Notifications\AddedToSiteNotification;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Mtc\MercuryDataModels\Role;
use Mtc\MercuryDataModels\Tenant;
use Mtc\MercuryDataModels\User;
use Tests\TestCase;

class InviteManagerTest extends TestCase
{
    use RefreshDatabase;

    protected bool $tenancy_make_databases = false;

    public function testAbleToInviteNewUserToSite()
    {
        Notification::fake();
        $inviteManager = $this->inviteManager();
        Role::query()->create(['name' => 'administrator']);

        $email = 'foo@example.com';
        User::query()->where('email', $email)->delete();
        $site = Tenant::create();
        $this->assertFalse($site->users()->where('email', $email)->exists());

        $inviteManager->invite($email, $site->id, 'administrator');

        $user = $site->users()->where('email', $email)->first();
        $this->assertInstanceOf(User::class, $user);
        Notification::assertSentTo($user, AddedToSiteNotification::class);
    }

    public function testAbleToInviteExistingUserToSite()
    {
        Notification::fake();
        $inviteManager = $this->inviteManager();
        Role::query()->create(['name' => 'administrator']);

        $user = User::factory()->create();
        $site = Tenant::create();
        $this->assertFalse($site->users()->where('email', $user->email)->exists());

        $inviteManager->invite($user->email, $site->id, 'administrator');

        $this->assertTrue($site->users()->where('email', $user->email)->exists());
        Notification::assertSentTo($user, AddedToSiteNotification::class);
    }

    public function testDoesNotInviteUserThatHasAccessToSite()
    {
        Notification::fake();
        $inviteManager = $this->inviteManager();
        Role::query()->create(['name' => 'administrator']);

        $user = User::factory()->create();
        $site = Tenant::create();
        $site->users()->attach($user->id, ['role' => 'administrator']);

        $inviteManager->invite($user->email, $site->id, 'administrator');

        Notification::assertNothingSent();
    }

    public function testFailsInviteWhenSiteDoesNotExist()
    {
        $inviteManager = $this->inviteManager();
        $user = User::factory()->create();
        Role::query()->create(['name' => 'administrator']);

        $this->expectException(ModelNotFoundException::class);
        $inviteManager->invite($user->email, PHP_INT_MAX, 'administrator');
    }

    public function testUserIsAssignedRole()
    {
        Notification::fake();
        $inviteManager = $this->inviteManager();
        Role::query()->create(['name' => 'administrator']);

        $user = User::factory()->create();
        $site = Tenant::create();

        $inviteManager->invite($user->email, $site->id, 'administrator');

        $user = $site->users()->where('email', $user->email)->first();
        $this->assertEquals('administrator', $user->pivot->role);
    }

    public function testAssigningBadRole()
    {
        Notification::fake();
        $inviteManager = $this->inviteManager();

        $user = User::factory()->create();
        $site = Tenant::create();

        $this->expectException(RoleNotFound::class);
        $inviteManager->invite($user->email, $site->id, 'administrator');

    }

    private function inviteManager(): InviteManager
    {
        /** @var InviteManager $manager */
        return app(InviteManager::class);
    }
}
