<?php

namespace Tests\Feature\Controllers;

use App\Notifications\AddedToSiteNotification;
use App\Tier;
use Carbon\Carbon;
use Database\Seeders\Global\GlobalRoleSeed;
use Illuminate\Support\Facades\Notification;
use Mtc\MercuryDataModels\Tenant;
use Mtc\MercuryDataModels\User;
use Tests\DatabaseTestCase;
use Tests\UserForTenant;

class SiteControllerTest extends DatabaseTestCase
{
    use UserForTenant;

    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testIndex()
    {
        Tenant::factory(5)->create();

        $response = $this->actingAs($this->getUser())->withoutExceptionHandling()
            ->getJson(route('sites.index', [], false));

        $response->assertStatus(200);
        $this->assertIsArray($response->json('data'));
        // 5 created + 1 from initializeTenancy()
        $this->assertCount(6, $response->json('data'));
    }

    public function testGetUserRoles()
    {
        $this->seed(GlobalRoleSeed::class);
        $response = $this->actingAs($this->getUser())
            ->getJson(route('sites.roles', [], false));

        $response->assertStatus(200);
        $this->assertIsArray($response->json());
        $this->assertCount(5, $response->json());
    }

    public function testCreateSite()
    {
        $response = $this->actingAs($this->getUser())
            ->postJson(route('sites.store', [], false), [
                'name' => 'foo baz',
                'tier' => Tier::LITE->value,
            ]);

        $response->assertStatus(200);
        $this->assertTrue(Tenant::query()->where('name', 'foo baz')->exists());
    }

    public function testInviteUser()
    {
        Notification::fake();
        $this->seed(GlobalRoleSeed::class);

        $tenant = Tenant::factory()->create();
        $response = $this->actingAs($this->getUser())
            ->postJson(route('sites.invite-user', $tenant, false), [
                'email' => 'jdoe@example.com',
                'role' => 'Administrator',
            ]);

        $response->assertStatus(200);

        $user = User::query()->where('email', 'jdoe@example.com')->first();
        $this->assertInstanceOf(User::class, $user);
        Notification::assertSentTo($user, AddedToSiteNotification::class);
    }

    public function testUpdateUser()
    {
        $tenant = Tenant::factory()->create();
        $user = User::factory()->create();
        $tenant->users()->attach($user, ['role' => 'Administrator']);

        $user_data = $tenant->users()->where('user_id', $user->id)->first();
        $this->assertEquals('Administrator', $user_data->pivot->role);

        $response = $this->actingAs($this->getUser())
            ->postJson(route('sites.update-user', $tenant, false), [
                'userId' => $user->id,
                'role' => 'Editor',
            ]);

        $response->assertStatus(200);
        $user_data = $tenant->users()->where('user_id', $user->id)->first();
        $this->assertEquals('Editor', $user_data->pivot->role);
    }

    public function testRemoveUser()
    {
        $tenant = Tenant::factory()->create();
        $user = User::factory()->create();
        $tenant->users()->attach($user);

        $this->assertTrue($tenant->users()->where('user_id', $user->id)->exists());

        $response = $this->actingAs($this->getUser())
            ->postJson(route('sites.remove-user', $tenant, false), [
                'userId' => $user->id
            ]);

        $response->assertStatus(200);
        $this->assertFalse($tenant->users()->where('user_id', $user->id)->exists());
    }

    public function testSuspend()
    {
        $tenant = Tenant::factory()->create([
            'suspended_at' => null,
            'suspended_by' => null,
        ]);

        $response = $this->actingAs($this->getUser())
            ->postJson(route('sites.suspend', $tenant, false));

        $response->assertStatus(200);
        $tenant->refresh();
        $this->assertTrue($tenant->is_suspended);
        $this->assertEquals($this->getUser()->id, $tenant->suspended_by);
    }

    public function testUnsuspend()
    {
        $tenant = Tenant::factory()->create([
            'suspended_at' => Carbon::now(),
            'suspended_by' => $this->getUser()->id,
        ]);

        $response = $this->actingAs($this->getUser())
            ->postJson(route('sites.unsuspend', $tenant, false));

        $response->assertStatus(200);
        $tenant->refresh();
        $this->assertFalse($tenant->is_suspended);
        $this->assertNull($tenant->suspended_by);
    }

    public function testToggleFavouriteSiteAddsToFavourites()
    {
        $tenant = Tenant::factory()->create();
        $user = $this->getUser();

        $this->assertFalse($user->favouriteSites()->where('tenant_id', $tenant->id)->exists());

        $response = $this->actingAs($user)
            ->postJson(route('users.toggle-favourite-site', [], false), [
                'site_id' => $tenant->id,
            ]);

        $response->assertStatus(200);
        $response->assertJson(['is_favourite' => true]);
        $this->assertTrue($user->favouriteSites()->where('tenant_id', $tenant->id)->exists());
    }

    public function testToggleFavouriteSiteRemovesFromFavourites()
    {
        $tenant = Tenant::factory()->create();
        $user = $this->getUser();
        $user->favouriteSites()->attach($tenant->id);

        $this->assertTrue($user->favouriteSites()->where('tenant_id', $tenant->id)->exists());

        $response = $this->actingAs($user)
            ->postJson(route('users.toggle-favourite-site', [], false), [
                'site_id' => $tenant->id,
            ]);

        $response->assertStatus(200);
        $response->assertJson(['is_favourite' => false]);
        $this->assertFalse($user->favouriteSites()->where('tenant_id', $tenant->id)->exists());
    }

    public function testIndexReturnsFavouriteSiteIds()
    {
        $tenants = Tenant::factory(3)->create();
        $user = $this->getUser();
        $user->favouriteSites()->attach($tenants[0]->id);
        $user->favouriteSites()->attach($tenants[2]->id);

        $response = $this->actingAs($user)
            ->getJson(route('sites.index', [], false));

        $response->assertStatus(200);
        $this->assertArrayHasKey('favourite_site_ids', $response->json());
        $this->assertCount(2, $response->json('favourite_site_ids'));
        $this->assertContains($tenants[0]->id, $response->json('favourite_site_ids'));
        $this->assertContains($tenants[2]->id, $response->json('favourite_site_ids'));
    }

    public function testIndexReturnsSitesWithIsFavouriteFlag()
    {
        $tenants = Tenant::factory(3)->create();
        $user = $this->getUser();
        $user->favouriteSites()->attach($tenants[0]->id);

        $response = $this->actingAs($user)
            ->getJson(route('sites.index', [], false));

        $response->assertStatus(200);

        $data = $response->json('data');
        $favouriteSite = collect($data)->firstWhere('id', $tenants[0]->id);
        $nonFavouriteSite = collect($data)->firstWhere('id', $tenants[1]->id);

        $this->assertTrue($favouriteSite['is_favourite']);
        $this->assertFalse($nonFavouriteSite['is_favourite']);
    }

    public function testIndexSortsFavouriteSitesFirst()
    {
        // Create sites with names that would normally sort alphabetically
        $tenantA = Tenant::factory()->create(['name' => 'AAA Site']);
        $tenantB = Tenant::factory()->create(['name' => 'BBB Site']);
        $tenantC = Tenant::factory()->create(['name' => 'CCC Site']);

        $user = $this->getUser();
        // Mark the last alphabetically as favourite
        $user->favouriteSites()->attach($tenantC->id);

        $response = $this->actingAs($user)
            ->getJson(route('sites.index', [], false));

        $response->assertStatus(200);

        $data = $response->json('data');
        // Favourite site (CCC) should be first
        $this->assertEquals($tenantC->id, $data[0]['id']);
        // Then alphabetically sorted non-favourites (there's also a tenant from initializeTenancy)
        // Find positions of our test tenants in the non-favourite portion
        $nonFavourites = array_slice($data, 1);
        $positionA = array_search($tenantA->id, array_column($nonFavourites, 'id'));
        $positionB = array_search($tenantB->id, array_column($nonFavourites, 'id'));
        $this->assertLessThan($positionB, $positionA, 'AAA Site should come before BBB Site');
    }

    public function testToggleFavouriteSiteRequiresSiteId()
    {
        $response = $this->actingAs($this->getUser())
            ->postJson(route('users.toggle-favourite-site', [], false), []);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors('site_id');
    }
}
