<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
use Tests\TestCase;
use Carbon\Carbon;

use App\Models\Livestream;
use App\Models\User;
use App\Models\Tag;
use App\Models\Inquiry;
use App\Models\Role;
use App\Models\FileUpload;
use App\Models\Photo;
use App\Models\LivestreamRegistration;
use App\Models\Location;

use App\Mail\LivestreamReminder;
use App\Mail\LivestreamRegistered;
use App\Mail\LivestreamModeratorAdded;
use App\Mail\LivestreamTimeChanged;

class LivestreamTest extends TestCase
{
    use WithFaker;

    public function test_the_livestream_index_can_be_loaded()
    {
        $this->get(route('livestreams.index'))
             ->assertRedirect('/login');

        $this->signIn(User::factory()->create());

        $this->withoutExceptionHandling();
        $this->get(route('livestreams.index'))
             ->assertRedirect('/');

        $this->signInAdmin();

        $this->get(route('livestreams.index'))
             ->assertSuccessful();
    }

    public function test_a_livestream_can_be_created()
    {
        $input = Livestream::factory()->raw();
        $role = Role::factory()->create();
        $mod = User::factory()->create();
        $input['roles'] = [$role];
        $input['moderators'] = [$mod];

        Storage::fake();
        $file_name = Str::lower(Str::random().'.jpg');
        $file = UploadedFile::fake()->image($file_name);
        $file_upload = (new FileUpload())->saveFile($file, 'photos', true);

        $photo_input = Photo::factory()->raw();
        $photo_input['file_upload_id'] = $file_upload->id;
        $input['photos'] = [$photo_input];


        $this->json('POST', route('livestreams.store'), [])
             ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('livestreams.store'), [])
             ->assertStatus(403);

        $this->signInAdmin();

        $this->json('POST', route('livestreams.store'), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'name',
                'start_date',
             ]);

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
             ]);

        $livestream = Livestream::all()->last();

        $this->assertNotNull($livestream->chat_mode);

        $this->assertInstanceOf(Livestream::class, $livestream);
        $this->assertEquals(Arr::get($input, 'name'), $livestream->name);
        $this->assertNotNull($livestream->description);
        $this->assertEquals(Arr::get($input, 'description'), $livestream->description);
        $this->assertEquals(Arr::get($input, 'video_id'), $livestream->video_id);
        $this->assertEquals(Arr::get($input, 'start_date'), $livestream->start_date);
        $this->assertInstanceOf(Carbon::class, $livestream->start_date);
        $this->assertEquals(Arr::get($input, 'length'), $livestream->length);
        $this->assertEquals(Arr::get($input, 'chat_mode'), $livestream->chat_mode);
        $this->assertNotNull($livestream->unlisted);
        $this->assertEquals(Arr::get($input, 'unlisted'), $livestream->unlisted);
        $this->assertNotNull($livestream->can_register);
        $this->assertEquals(Arr::get($input, 'can_register'), $livestream->can_register);
        $this->assertNotNull($livestream->roles);
        $this->assertTrue($livestream->roles->contains('id', $role->id));
        $this->assertNotNull($livestream->moderators);
        $this->assertTrue($livestream->moderators->contains('id', $mod->id));
        $this->assertTrue($role->canPerformAction('view', $livestream));

        $this->assertEquals(1, $livestream->photos->count());

        $this->assertTrue($livestream->photos->contains(function ($p) use ($file_name) {
            return $p->fileUpload->name === $file_name;
        }));

        $photo = $livestream->photos->first();
        $this->assertInstanceOf(Livestream::class, $photo->content);
        $this->assertEquals($livestream->id, $photo->content->id);

        //Storage::assertExists('photos/'.$file->hashName());
        $this->assertInstanceOf(Photo::class, $photo);
        $this->assertEquals(Arr::get($photo_input, 'name'), $photo->name);
        $this->assertEquals(Arr::get($photo_input, 'description'), $photo->description);
        $this->assertEquals(Arr::get($photo_input, 'alt'), $photo->alt);
        $this->assertEquals($photo->fileUpload->id, $file_upload->id);
    }

    public function test_a_mod_gets_an_email_when_they_have_been_added_to_a_livestream()
    {
        $input = Livestream::factory()->raw();
        $mod = User::factory()->create();
        $input['moderators'] = [$mod];

        $this->signInAdmin();

        Mail::fake();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
             ]);

        $livestream = Livestream::all()->last();

        $this->assertInstanceOf(Livestream::class, $livestream);
        $this->assertNotNull($livestream->moderators);
        $this->assertTrue($livestream->moderators->contains('id', $mod->id));
        $this->assertTrue($mod->canPerformAction('moderate', $livestream));

        Mail::assertQueued(LivestreamModeratorAdded::class, function ($mail) use ($mod) {
            return $mail->hasTo($mod->email);
        });
    }

    public function test_a_livestream_can_be_updated()
    {
        $livestream = Livestream::factory()->create();
        $role = Role::factory()->create();
        $mod1 = User::factory()->create();
        $mod2 = User::factory()->create();
        $livestream->createPermission('view', $role);
        $this->assertTrue($role->canPerformAction('view', $livestream));
        $livestream->createPermission('moderate', $mod1);

        $new_role = Role::factory()->create();

        $input = Livestream::factory()->raw();
        $input['roles'] = [$new_role];
        $input['moderators'] = [$mod2];

        $this->json('POST', route('livestreams.update', ['id' => $livestream->id]), [])
             ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('livestreams.update', ['id' => $livestream->id]), [])
             ->assertStatus(403);

        $this->signInAdmin();

        $this->json('POST', route('livestreams.update', ['id' => $livestream->id]), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'name',
             ]);

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.update', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
                'name' => Arr::get($input, 'name'),
             ]);

        $livestream->refresh();

        $this->assertEquals(Arr::get($input, 'name'), $livestream->name);
        $this->assertNotNull($livestream->description);
        $this->assertEquals(Arr::get($input, 'description'), $livestream->description);
        $this->assertEquals(Arr::get($input, 'video_id'), $livestream->video_id);
        $this->assertEquals(Arr::get($input, 'start_date'), $livestream->start_date);
        $this->assertEquals(Arr::get($input, 'length'), $livestream->length);
        $this->assertEquals(Arr::get($input, 'enable_chat'), $livestream->enable_chat);
        $this->assertFalse($livestream->roles->contains('id', $role->id));
        $this->assertTrue($livestream->roles->contains('id', $new_role->id));
        $this->assertInstanceOf(Role::class, Role::find($role->id));
        $this->assertFalse($livestream->moderators->contains('id', $mod1->id));
        $this->assertTrue($livestream->moderators->contains('id', $mod2->id));
        $role->refresh();
        $new_role->refresh();
        $this->assertFalse($role->canPerformAction('view', $livestream));
        $this->assertTrue($new_role->canPerformAction('view', $livestream));
    }

    public function test_a_livestream_can_have_tags_that_are_used_for_filtering_on_pages()
    {
        $livestream = Livestream::factory()->create();
        $tag = Tag::factory()->create();

        $input = $livestream->toArray();
        $input['tags'] = [$tag];

        $this->signInAdmin();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.update', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
                'name' => Arr::get($input, 'name'),
             ]);

        $livestream->refresh();

        $this->assertNotNull($livestream->tags);
        $this->assertEquals(1, $livestream->tags->count());
        $this->assertTrue($livestream->tags->contains('id', $tag->id));
    }

    public function test_livestreams_can_be_loaded_for_pagination()
    {
        $livestream = Livestream::factory()->create();
        $inquiry = Inquiry::factory()->create();

        $inquiry->saveLivestreams(['livestream' => $livestream]);

        $this->assertTrue($livestream->users()->get()->contains('id', $inquiry->user->id));

        $this->json('GET', route('livestreams.index'))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('GET', route('livestreams.index'))
            ->assertStatus(403);

        $this->signInAdmin();
        $this->enableEditing();

        $this->withoutExceptionHandling();
        $this->json('GET', route('livestreams.index', ['paginate_count' => 1000]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
                'name' => $inquiry->user->name,
             ]);
    }

    public function test_a_livestream_can_be_viewed()
    {
        $livestream = Livestream::factory()->create();

        $this->withoutExceptionHandling();
        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream');

        $this->signInAdmin();
        $this->enableEditing();

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertSessionMissing('editing');
    }

    public function test_registering_for_a_livestream()
    {
        $livestream = Livestream::factory()->create();

        $this->withoutExceptionHandling();
        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream', $livestream);
        //->assertSee($livestream->name);
    }

    public function test_a_livestream_cannot_be_registered_for_after_it_has_completed()
    {
        $livestream = Livestream::factory()->create([
            'start_date' => now()->subMinutes(15),
            'length' => null,
        ]);

        $this->assertNotNull($livestream->hasCompleted);
        $this->assertTrue($livestream->hasCompleted);

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertRedirect()
            ->assertSessionHas([
                'error' => 'That livestream has completed',
            ]);

        $livestream->length = 30;
        $livestream->save();

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream', $livestream);

        $user = User::factory()->create();
        $this->signIn($user);

        $livestream->length = 0;
        $livestream->save();

        $this->assertFalse($user->can('register', $livestream));

        $livestream->length = 30;
        $livestream->save();

        $this->assertTrue($user->can('register', $livestream));
    }


    public function test_a_user_needs_permission_to_register_for_an_unlisted_livestream()
    {
        $user = User::factory()->create();

        $role = Role::factory()->create();
        $role_user = User::factory()->create();
        $role_user->addRole($role);
        $role_user->refresh();

        $livestream = Livestream::factory()->create([
            'unlisted' => true,
        ]);

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
            ->assertRedirect('/login');

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), [])
            ->assertStatus(422);

        $livestream->createPermission('view', $user);
        $livestream->refresh();
        $this->assertTrue($livestream->users->contains('id', $user->id));

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertRedirect('/login')
            ->assertSessionHas('url.intended', route('livestreams.register', ['id' => $livestream->id]));

        $this->signIn(User::factory()->create());

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertRedirect('/');

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), ['user_id' => auth()->user()->id])
            ->assertRedirect('/');

        $this->signIn($user);

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewIs('pages.view');

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), ['user_id' => auth()->user()->id])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ]);

        $this->signIn($role_user);

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertRedirect('/');

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), ['user_id' => auth()->user()->id])
            ->assertRedirect('/');

        $livestream->refresh();
        $livestream->createPermission('view', $role);
        $livestream->refresh();
        $role->refresh();
        $role_user->refresh();
        $this->assertTrue($livestream->roles->contains('id', $role->id));

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewIs('pages.view');

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), ['user_id' => auth()->user()->id])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ]);

        $inquiry = Inquiry::factory()->create();
        $inquiry_user = $inquiry->user;

        $this->signIn($inquiry_user);

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertRedirect('/');

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), ['user_id' => auth()->user()->id])
            ->assertRedirect('/');

        $inquiry->saveLivestreams([
            'livestream' => $livestream,
        ]);

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream')
             ->assertViewIs('pages.view');

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), ['user_id' => auth()->user()->id])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ]);
    }

    public function test_a_livestream_can_load_a_user()
    {
        $livestream = Livestream::factory()->create();
        $inquiry = Inquiry::factory()->create();

        $inquiry->saveLivestreams([
            'livestream' => $livestream,
        ]);

        $inquiry->refresh();
        $livestream->refresh();

        $this->assertTrue($inquiry->user->livestreams->contains('id', $livestream->id));
        $this->assertTrue($livestream->inquiryUsers()->get()->contains('id', $inquiry->user->id));

        $this->assertNotNull($livestream->livestreamRegistrations()->first()->url);
        $livestream_registration = $livestream->livestreamRegistrations()->first();
        $this->assertEquals($inquiry->user->id, $livestream_registration->user->id);
        $url = $livestream_registration->url;

        $this->get(route('livestreams.view', ['id' => $livestream]))
             ->assertSuccessful()
            ->assertViewMissing('inquiry', $inquiry);

        $this->withoutExceptionHandling();
        $this->get($url)
             ->assertSuccessful()
            ->assertViewHas('user', $inquiry->user);

        $this->assertTrue(auth()->check());
        $this->assertEquals($inquiry->user->id, auth()->user()->id);
    }

    public function test_a_livestream_with_permissions_can_only_be_viewed_by_those_users()
    {
        $user = User::factory()->create();

        $role = Role::factory()->create();
        $role_user = User::factory()->create();
        $role_user->addRole($role);
        $role_user->refresh();

        $livestream = Livestream::factory()->create([
            'unlisted' => true,
        ]);

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
            ->assertRedirect('/login');

        $livestream->createPermission('view', $user);
        $livestream->refresh();
        $this->assertTrue($livestream->users->contains('id', $user->id));

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertRedirect('/login')
            ->assertSessionHas('url.intended', route('livestreams.view', ['id' => $livestream->id]));

        $this->signIn(User::factory()->create());

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertRedirect('/');

        $this->signIn($user);

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream');

        $this->signIn($role_user);

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertRedirect('/');

        $livestream->refresh();
        $livestream->createPermission('view', $role);
        $this->assertTrue($livestream->roles->contains('id', $role->id));
        $role_user->refresh();

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream');

        $inquiry = Inquiry::factory()->create();
        $inquiry_user = $inquiry->user;

        $this->signIn($inquiry_user);

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertRedirect('/');

        $inquiry->saveLivestreams([
            'livestream' => $livestream,
        ]);

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream');
    }

    public function test_a_reminder_email_can_be_sent_to_inquiry_users_for_a_livestream()
    {
        $inquiry = Inquiry::factory()->create();
        $livestream = Livestream::factory()->create();

        $manager = User::factory()->create();
        $manager->addRole('livestreams-manager');
        $manager->refresh();

        $inquiry->saveLivestreams([
            'livestream' => $livestream,
        ]);

        $this->json('POST', route('livestreams.reminder-emails', ['id' => $livestream->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('livestreams.reminder-emails', ['id' => $livestream->id]))
            ->assertStatus(403);

        $this->signIn($manager);

        Mail::fake();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.reminder-emails', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => $livestream->users->count().' Reminder Emails Queued To Send',
             ]);

        Mail::assertQueued(LivestreamReminder::class, function ($mail) use ($inquiry) {
            return $mail->hasTo($inquiry->user->email);
        });

        $livestream->refresh();
        $this->assertEquals(1, $livestream->users->count());
        $livestream_registration = $livestream->livestreamRegistrations()->where('user_id', $inquiry->user->id)->first();

        $this->assertNotNull($livestream_registration->reminder_email_sent_at);
    }


    public function test_an_individual_reminder_can_be_sent_to_a_livestream_user()
    {
        $inquiry1 = Inquiry::factory()->create();
        $inquiry2 = Inquiry::factory()->create();
        $livestream = Livestream::factory()->create();

        $inquiry1->saveLivestreams([
            'livestream' => $livestream,
        ]);

        $inquiry2->saveLivestreams([
            'livestream' => $livestream,
        ]);

        $mod = User::factory()->create();
        $livestream->createPermission('moderate', $mod);
        $mod->refresh();

        $this->signIn($mod);

        Mail::fake();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.reminder-emails', ['id' => $livestream->id]), ['user_ids' => [$inquiry1->user->id]])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => '1 Reminder Emails Queued To Send',
             ]);

        Mail::assertQueued(LivestreamReminder::class, function ($mail) use ($inquiry1) {
            return $mail->hasTo($inquiry1->user->email);
        });

        Mail::assertNotQueued(LivestreamReminder::class, function ($mail) use ($inquiry2) {
            return $mail->hasTo($inquiry2->user->email);
        });
    }

    public function test_anyone_can_regsiter_for_a_public_livestream()
    {
        $livestream = Livestream::factory()->create();

        $this->get(route('livestreams.register', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream', $livestream);

        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), [])
            ->assertStatus(422)
            ->assertJsonValidationErrors([
                'name',
                'email'
            ]);

        $name = $this->faker->name;
        $email = $this->faker->unique()->safeEmail;

        $input = [
            'name' => $name,
            'email' => $email,
        ];

        Mail::fake();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ])
             ->assertJsonStructure(['success', 'redirect']);

        Mail::assertQueued(LivestreamRegistered::class, function ($mail) use ($email) {
            return $mail->hasTo($email);
        });

        $livestream_registration = $livestream->livestreamRegistrations()->get()->last();
        $this->assertInstanceOf(LivestreamRegistration::class, $livestream_registration);
        $this->assertInstanceOf(User::class, $livestream_registration->user);
        $this->assertEquals($name, $livestream_registration->user->name);
        $this->assertEquals($email, $livestream_registration->user->email);
        $this->assertInstanceOf(Location::class, $livestream_registration->location);

        $this->assertInstanceOf(Livestream::class, $livestream_registration->livestream);
        $this->assertEquals($livestream->id, $livestream_registration->livestream->id);

        $user = User::where('email', $email)->first();

        $this->assertInstanceOf(User::class, $user);
        $this->assertTrue($user->livestreams->contains('id', $livestream->id));
        $livestream->refresh();
        $this->assertTrue($livestream->users->contains('id', $user->id));
    }

    public function test_a_user_cannot_register_for_a_livestream_twice()
    {
        $user = User::factory()->create();
        $livestream = Livestream::factory()->create();

        $livestream->registerUser($user);

        $user->refresh();
        $livestream->refresh();

        $this->assertEquals(1, $user->livestreams->count());
        $this->assertEquals($livestream->id, $user->livestreams->first()->id);

        Mail::fake();

        $input = [
            'name' => $user->name,
            'email' => $user->email,
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ])
             ->assertJsonStructure(['success', 'redirect']);

        Mail::assertQueued(LivestreamRegistered::class, function ($mail) use ($user) {
            return $mail->hasTo($user->email);
        });

        $user->refresh();
        $livestream->refresh();

        $this->assertEquals(1, $user->livestreams->count());
        $this->assertEquals($livestream->id, $user->livestreams->first()->id);
    }

    public function test_registering_with_a_used_email_address_doesnt_create_a_new_account()
    {
        $user = User::factory()->create();
        $livestream = Livestream::factory()->create();

        Mail::fake();

        $input = [
            'name' => 'foobar',
            'email' => $user->email,
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ]);

        Mail::assertQueued(LivestreamRegistered::class, function ($mail) use ($user) {
            return $mail->hasTo($user->email);
        });

        $last_user = User::all()->last();

        $this->assertEquals($user->id, $last_user->id);
        $this->assertTrue($livestream->users->contains('id', $user->id));
    }

    public function test_if_a_user_is_logged_in_and_registers_for_a_livestream_they_dont_have_to_fill_in_the_form()
    {
        $user = User::factory()->create();
        $livestream = Livestream::factory()->create();

        $this->signIn($user);

        $input = [
            'user_id' => $user->id,
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ]);

        $livestream->refresh();
        $this->assertTrue($livestream->users->contains('id', $user->id));
    }

    public function test_the_livestream_public_index_can_be_viewed()
    {
        $livestream = Livestream::factory()->create();

        $this->withoutExceptionHandling();
        $this->get('/live')
             ->assertSuccessful();
    }

    public function test_the_livestream_registration_complete_page_can_be_viewed()
    {
        $livestream = Livestream::factory()->create();

        $name = $this->faker->name;
        $email = $this->faker->unique()->safeEmail;

        $input = [
            'name' => $name,
            'email' => $email,
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.register', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Registration Complete',
             ])
             ->assertJsonStructure(['success', 'redirect']);

        $livestream->refresh();
        $user = $livestream->users->last();

        $this->get(route('livestreams.registration-complete', ['id' => $livestream->id]))
             ->assertSuccessful()
                ->assertViewHas([
                    'livestream' => $livestream,
                ]);
    }


    public function test_the_livestream_list_only_shows_public_or_permissioned_livestreams_after_sign_in()
    {
        $user = User::factory()->create();

        $role = Role::factory()->create();
        $role_user = User::factory()->create();
        $role_user->addRole($role);
        $role_user->refresh();

        $livestream = Livestream::factory()->create([
            'unlisted' => true,
        ]);

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonMissing([
                'name' => $livestream->name,
            ]);

        $livestream->createPermission('view', $user);
        $livestream->refresh();
        $this->assertTrue($livestream->users->contains('id', $user->id));

        $this->signIn(User::factory()->create());

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonMissing([
                'name' => $livestream->name,
            ]);

        $this->signIn($user);

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream->name,
            ]);

        $this->signIn($role_user);

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
            ->assertSuccessful()
            ->assertJsonMissing([
                'name' => $livestream->name,
            ]);

        $livestream->refresh();
        $livestream->createPermission('view', $role);
        $this->assertTrue($livestream->roles->contains('id', $role->id));
        $role_user->refresh();

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream->name,
            ]);

        $inquiry = Inquiry::factory()->create();
        $inquiry_user = $inquiry->user;

        $this->signIn($inquiry_user);

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonMissing([
                'name' => $livestream->name,
            ]);

        $inquiry->saveLivestreams(['livestream' => $livestream]);

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream->name,
            ]);
    }

    public function test_livestreams_can_be_paginated_and_fitlered_by_tag()
    {
        $tag = Tag::factory()->create();
        $livestream = Livestream::factory()->create();
        $livestream2 = Livestream::factory()->create();

        $livestream->addTag($tag);

        $this->json('POST', route('livestreams.paginate'), ['paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream->name,
                'name' => $livestream2->name,
            ]);

        $this->json('POST', route('livestreams.paginate'), ['tags' => [$tag], 'paginate' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream->name,
            ])
            ->assertJsonMissing([
                'name' => $livestream2->name,
            ]);
    }

    public function test_livestreams_can_be_exclude_by_tag()
    {
        $livestream1 = Livestream::factory()->create();
        $tag1 = Tag::factory()->create();
        $livestream1->addTag($tag1);
        $livestream1->refresh();

        $livestream2 = Livestream::factory()->create();
        $tag2 = Tag::factory()->create();
        $livestream2->addTag($tag2);
        $livestream2->refresh();

        $livestream3 = Livestream::factory()->create();
        $tag3 = Tag::factory()->create();
        $livestream3->addTag($tag3);
        $livestream3->refresh();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.paginate'), ['paginate_count' => Livestream::count(), 'paginate' => true,
                                                'tags' => [
                                                       ['id' => $tag1->id ],
                                                       ['id' => $tag3->id ],
                                                       ['id' => $tag2->id, 'pivot' => ['exclude' =>  true ]],
                                                ]])
        ->assertSuccessful()
        ->assertJsonFragment([
            'name' => $livestream1->name,
            'name' => $livestream3->name,
        ])
        ->assertJsonMissing([
            'name' => $livestream2->name,
        ]);
    }

    public function test_livestreams_can_be_filtered_by_past_and_present()
    {
        $livestream_future = Livestream::factory()->create([
            'start_date' => now()->addHour(4),
        ]);

        $livestream_past = Livestream::factory()->create([
            'start_date' => now()->subHour(4),
        ]);

        $this->json('POST', route('livestreams.paginate'), ['show_future' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream_future->name,
            ])
            ->assertJsonMissing([
                'name' => $livestream_past->name,
            ]);

        $this->json('POST', route('livestreams.paginate'), ['show_past' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream_past->name,
            ])
            ->assertJsonMissing([
                'name' => $livestream_future->name,
            ]);

        $this->json('POST', route('livestreams.paginate'), ['paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream_past->name,
                'name' => $livestream_future->name,
            ]);

        $this->json('POST', route('livestreams.paginate'), ['show_past' => true, 'show_future' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream_past->name,
                'name' => $livestream_future->name,
            ]);
    }

    public function test_a_livestreams_length_effects_the_lists_for_past_and_future()
    {
        $livestream = Livestream::factory()->create([
            'start_date' => now()->subMinutes(15),
            'length' => null,
        ]);

        $this->json('POST', route('livestreams.paginate'), ['show_future' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonMissing([
                'name' => $livestream->name,
            ]);

        $this->json('POST', route('livestreams.paginate'), ['show_past' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream->name,
            ]);

        $livestream->length = 30;
        $livestream->save();
        $livestream->refresh();

        $this->json('POST', route('livestreams.paginate'), ['show_future' => true, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream->name,
            ]);
    }

    public function test_a_livestream_can_be_excluded_from_the_pagination_results()
    {
        $livestream1 = Livestream::factory()->create();
        $livestream2 = Livestream::factory()->create();

        $this->json('POST', route('livestreams.paginate'), ['exclude_id' => $livestream2->id, 'paginate_count' => 1000])
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $livestream1->name,
            ])
            ->assertJsonMissing([
                'name' => $livestream2->name,
            ]);
    }

    public function test_a_user_can_unregister_from_a_livestream()
    {
        $user = User::factory()->create();
        $livestream = Livestream::factory()->create();

        $livestream->registerUser($user);
        $livestream->refresh();

        // we need a valid signed url to complete
        $this->get(route('livestreams.unregister', ['id' => $livestream->id, 'user_id' => $user->id]))
            ->assertStatus(401);

        $url = $livestream->livestreamRegistrations()->firstWhere('user_id', $user->id)->unregister_url;

        $this->assertNotNull($url);

        $this->get($url)
            ->assertSuccessful()
            ->assertViewHas([
                'livestream',
                'user',
            ]);

        $livestream->refresh();
        $user->refresh();

        $this->assertFalse($user->can('view', $livestream));
        $this->assertFalse($livestream->livestreamRegistrations()->get()->contains('user_id', $user->id));
    }

    public function test_changing_a_start_date_for_a_livestream_sends_an_update_email_to_users()
    {
        $livestream = Livestream::factory()->create();
        $user = User::factory()->create();

        $livestream->registerUser($user);

        $livestream->refresh();

        $this->assertTrue($livestream->users->contains('id', $user->id));

        Mail::fake();

        $input = Livestream::factory()->raw();
        $new_start_date = $livestream->start_date->addDays(7);
        $input['start_date'] = $new_start_date;

        $this->signInAdmin();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.update', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
                'name' => Arr::get($input, 'name'),
             ]);

        $livestream->refresh();

        Mail::assertQueued(LivestreamTimeChanged::class, function ($mail) use ($user) {
            return $mail->hasTo($user->email);
        });
    }

    public function test_changing_a_livestreams_length_shouldnt_send_a_time_changed_email()
    {
        $livestream = Livestream::factory()->create();
        $user = User::factory()->create();

        $livestream->registerUser($user);

        $livestream->refresh();

        $this->assertTrue($livestream->users->contains('id', $user->id));

        Mail::fake();

        $input = $livestream->toArray();
        $input['length'] = $this->faker->numberBetween(10, 100);

        $this->signInAdmin();

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.update', ['id' => $livestream->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
                'name' => Arr::get($input, 'name'),
             ]);

        $livestream->refresh();

        Mail::assertNotQueued(LivestreamTimeChanged::class, function ($mail) use ($user) {
            return $mail->hasTo($user->email);
        });
    }

    public function test_a_user_is_marked_as_attended_when_they_join_a_livestream_through_the_hash()
    {
        $user = User::factory()->create();
        $livestream = Livestream::factory()->create();

        $livestream->registerUser($user);

        $this->assertNotNull($livestream->livestreamRegistrations()->first()->url);
        $livestream_registration = $livestream->livestreamRegistrations()->first();
        $this->assertEquals($user->id, $livestream_registration->user->id);
        $url = $livestream_registration->url;

        $this->get($url)
             ->assertSuccessful();

        $livestream_registration->refresh();

        $this->assertNotNull($livestream_registration->attended_at);
    }

    public function test_a_user_is_marked_as_attended_when_they_join_a_livestream_when_they_are_logged_in()
    {
        $user = User::factory()->create();
        $livestream = Livestream::factory()->create();

        $livestream->registerUser($user);
        $livestream_registration = $livestream->livestreamRegistrations()->where('user_id', $user->id)->first();

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertSuccessful();

        $livestream_registration->refresh();
        $this->assertNull($livestream_registration->attended_at);

        $this->signIn($user);

        $this->get(route('livestreams.view', ['id' => $livestream->id]))
             ->assertSuccessful()
             ->assertViewHas('livestream');

        $livestream_registration->refresh();
        $this->assertNotNull($livestream_registration->attended_at);
    }

    public function test_a_user_can_login_from_the_livestream_reminder_link()
    {
        $user = User::factory()->create();
        $livestream = Livestream::factory()->create();

        $livestream->registerUser($user);

        $livestream_registration = $livestream->livestreamRegistrations->where('user_id', $user->id)->first();
        $this->assertInstanceOf(LivestreamRegistration::class, $livestream_registration);

        $url = $livestream_registration->url;

        $this->assertNotNull($url);

        $this->get($url)
             ->assertSuccessful()
            ->assertSessionHas('timeout');
    }

    public function test_a_livestreams_manager_can_load_the_index()
    {
        $user = User::factory()->create();

        $this->get(route('livestreams.index'))
             ->assertRedirect('/login');

        $this->signIn($user);

        $this->get(route('livestreams.index'))
             ->assertRedirect('/');

        $user->addRole('livestreams-manager');
        $user->refresh();

        $this->assertTrue($user->can('viewAny', Livestream::class));

        $this->get(route('livestreams.index'))
            ->assertSuccessful();

        $this->json('GET', route('livestreams.index'))
            ->assertSuccessful();
    }

    public function test_a_livestream_manager_can_create_a_livestream()
    {
        $user = User::factory()->create();

        $input = Livestream::factory()->raw();

        $this->json('POST', route('livestreams.store'), [])
            ->assertStatus(401);

        $this->signIn($user);

        $this->json('POST', route('livestreams.store'), [])
            ->assertStatus(403);

        $user->addRole('livestreams-manager');
        $user->refresh();

        $this->assertTrue($user->can('create', Livestream::class));

        $this->json('POST', route('livestreams.store'), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'name',
                'start_date',
                'chat_mode',
                'unlisted',
             ]);

        $this->json('POST', route('livestreams.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
             ]);

        $livestream = Livestream::all()->last();

        $this->assertEquals(Arr::get($input, 'name'), $livestream->name);
        $this->assertEquals(Arr::get($input, 'video_id'), $livestream->video_id);
        $this->assertEquals(Arr::get($input, 'start_date'), $livestream->start_date);
        $this->assertEquals(Arr::get($input, 'chat_mode'), $livestream->chat_mode);
        $this->assertEquals(Arr::get($input, 'unlisted'), $livestream->unlisted);
    }

    public function test_a_livestream_manager_can_search_for_users_roles()
    {
        $manager = User::factory()->create();

        $this->json('POST', route('users.search'), [])
            ->assertStatus(401);

        $this->json('POST', route('roles.search'), [])
            ->assertStatus(401);

        $this->signIn($manager);

        // Search Users
        $user = User::factory()->create();
        $input = [
            'autocomplete' => true,
            'terms' => $user->name,
        ];

        $this->json('POST', route('users.search'), $input)
            ->assertStatus(403);

        $manager->addRole('livestreams-manager');
        $manager->refresh();

        $this->json('POST', route('users.search'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                 'name' => $user->name,
                 'class_name' => $user->class_name,
             ]);

        // Search Roles
        $role = Role::factory()->create();
        $input = [
            'autocomplete' => true,
            'terms' => $role->name,
        ];

        $manager->removeRole('livestreams-manager');
        $manager->refresh();

        $this->json('POST', route('roles.search'), $input)
            ->assertStatus(403);

        $manager->addRole('livestreams-manager');
        $manager->refresh();

        $this->json('POST', route('roles.search'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                 'name' => $role->name,
                 'class_name' => $role->class_name,
             ]);
    }

    public function test_livestreams_can_be_searched_for()
    {
        $livestream = Livestream::factory()->create();
        $livestream2 = Livestream::factory()->create();

        $this->assertNotNull($livestream->name);

        $input = [
            'terms' => substr($livestream->name, 0, 8),
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('livestreams.search'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
             ])
             ->assertJsonMissing([
                'name' => $livestream2->name,
             ]);
    }

    public function test_upcoming_livestreams_can_be_search_for_by_name()
    {
        $tag = Tag::where('name', 'Online Open House')->first();
        $mod = User::factory()->create();
        $mod->addRole('livestreams-manager');

        $livestream = Livestream::factory()->create([
            'start_date' => now()->roundMinutes(10),
            'unlisted' => false,
            'can_register' => true,
            'chat_mode' => 'private',
            'length' => 60,
        ]);

        $livestream->tags()->attach($tag, ['exclude' => false]);
        $livestream->createPermission('moderate', $mod);

        $livestream->refresh();

        $input = [
            'terms' => $livestream->name,
        ];

        $this->json('POST', route('livestreams.search'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
             ]);
    }

    public function test_managers_can_lazy_load_registered_users_when_searching_for_livestreams()
    {
        $mod = User::factory()->create();
        $mod->addRole('livestreams-manager');
        $mod->refresh();
        $tag = Tag::factory()->create();

        $livestream = Livestream::factory()->create([
            'start_date' => now()->roundMinutes(10),
            'unlisted' => false,
            'can_register' => true,
            'chat_mode' => 'private',
            'length' => 60,
        ]);

        $livestream->tags()->attach($tag, ['exclude' => false]);
        $livestream->createPermission('moderate', $mod);

        $livestream->refresh();

        $user = User::factory()->create();
        $livestream->registerUser($user);

        $input = [
            'terms' => $livestream->name,
        ];

        $this->json('POST', route('livestreams.search'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
             ])
             ->assertJsonMissing([
                'name' => $user->name,
             ]);

        $this->signIn($mod);
        $this->assertTrue(auth()->user()->can('manage', Livestream::class));

        $this->json('POST', route('livestreams.search'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
                'name' => $user->name,
             ]);
    }

    public function test_a_livestream_can_be_searched_for_with_two_words()
    {
        $word1 = $this->faker->word();
        $word2 = $this->faker->word();
        if (strlen($word1) < 3) {
            $word1 .= $this->faker->word();
        }
        if (strlen($word2) < 3) {
            $word2 .= $this->faker->word();
        }
        $name = $word1.' '.$word2;
        $livestream = Livestream::factory()->create([
            'name' => $name,
            'unlisted' => false,
        ]);

        $this->json('POST', route('livestreams.search'), ['terms' => $word1])
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
             ]);

        $this->json('POST', route('livestreams.search'), ['terms' => $word2])
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
             ]);

        $this->json('POST', route('livestreams.search'), ['terms' => $name])
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $livestream->name,
             ]);
    }

    /*
    public function pulling_info_from_youtube_api()
    {
        $response = Http::get('https://www.googleapis.com/youtube/v3/search?eventType=completed&part=snippet&channelId=UCBqYoIrm5pvK5tDjYlqEOhw&type=video&key='.env('YOUTUBE_API_KEY'));

        //dd($response->json());
    }
     */
}
