<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

use App\Models\PageRedirect;
use App\Models\User;
use App\Models\Page;
use App\Models\Blog;
use App\Models\TextBlock;

class PageRedirectTest extends TestCase
{
    public function test_a_page_redirect_can_be_created(): void
    {
        $input = PageRedirect::factory()->raw();

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

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

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

        $this->signInAdmin();

        $this->json('POST', route('page-redirects.store'), [])
             ->assertStatus(422)
            ->assertJsonValidationErrors([
                'url_regex',
                'redirect',
                'sort_order',
            ]);

        $this->json('POST', route('page-redirects.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Redirect Saved',
             ]);

        $page_redirect = PageRedirect::all()->last();

        $this->assertNotNull($page_redirect->url_regex);
        $this->assertNotNull($page_redirect->redirect);

        $this->assertEquals(Arr::get($input, 'url_regex'), $page_redirect->url_regex);
        $this->assertEquals(Arr::get($input, 'redirect'), $page_redirect->redirect);
        $this->assertEquals(Arr::get($input, 'sort_order'), $page_redirect->sort_order);
    }

    public function test_a_page_redirect_can_be_removed(): void
    {
        $page_redirect = PageRedirect::factory()->create();

        $this->json('POST', route('page-redirects.remove', ['id' => $page_redirect->id]))
            ->assertStatus(401);

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

        $this->json('POST', route('page-redirects.remove', ['id' => $page_redirect->id]))
            ->assertStatus(403);

        $this->signInAdmin();

        $this->json('POST', route('page-redirects.remove', ['id' => $page_redirect->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Redirect Removed',
             ]);

        $this->assertNull(PageRedirect::find($page_redirect->id));
    }

    public function test_the_page_redirect_index_can_be_loaded(): void
    {
        $this->get(route('page-redirects.index'))
            ->assertRedirect();

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

        $this->get(route('page-redirects.index'))
            ->assertRedirect();

        $this->signInAdmin();

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

        $page_redirect = PageRedirect::factory()->create();

        $this->json('GET', route('page-redirects.index'))
             ->assertSuccessful()
            ->assertJsonFragment([
                'url_regex' => $page_redirect->url_regex,
            ]);
    }

    public function test_a_page_redirect_can_be_updated(): void
    {
        $page_redirect = PageRedirect::factory()->create();
        $input = PageRedirect::factory()->raw([
            'sort_order' => $page_redirect->sort_order,
        ]);

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

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

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

        $this->signInAdmin();

        $this->json('POST', route('page-redirects.update', ['id' => $page_redirect->id]), [])
             ->assertStatus(422)
            ->assertJsonValidationErrors([
                'url_regex',
                'redirect',
                'sort_order',
            ]);

        $this->json('POST', route('page-redirects.update', ['id' => $page_redirect->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Redirect Saved',
             ]);

        $page_redirect->refresh();

        $this->assertNotNull($page_redirect->url_regex);
        $this->assertNotNull($page_redirect->redirect);

        $this->assertEquals(Arr::get($input, 'url_regex'), $page_redirect->url_regex);
        $this->assertEquals(Arr::get($input, 'redirect'), $page_redirect->redirect);
        $this->assertEquals(Arr::get($input, 'sort_order'), $page_redirect->sort_order);
    }

    public function test_a_page_can_be_have_a_redirect_url(): void
    {
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $page->publish();

        $page_redirect = PageRedirect::factory()->create([
            'redirect' => $page->id,
        ]);

        $this->assertNotNull($content_element->content->header);

        $this->get($page_redirect->url_regex)
             ->assertStatus(301)
             ->assertRedirect($page->full_slug);
    }

    public function test_an_old_news_url_is_redirect(): void
    {
        $blog = Blog::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $blog);
        $blog->publish();

        $page_redirect = PageRedirect::factory()->create([
            'url_regex' => '^news/single-page-news/article/(.*)$',
            'redirect' => 'blogs/$1',
        ]);

        $url = 'news/single-page-news/article/'.$blog->getSlug();

        $this->get($url)
             ->assertRedirect($blog->full_slug);
    }

    public function test_saving_a_page_redirect_resorts_all_redirects(): void
    {
        if (!PageRedirect::all()->count()) {
            PageRedirect::factory()->create();
        }

        PageRedirect::sortAll();

        $inital_count = PageRedirect::all()->count();

        $page_redirect1 = PageRedirect::factory()->create([
            'sort_order' => $inital_count + 1,
        ]);

        $page_redirect2 = PageRedirect::factory()->create([
            'sort_order' => $inital_count + 2,
        ]);

        $page_redirect3 = PageRedirect::factory()->create([
            'sort_order' => $inital_count + 3,
        ]);

        $this->signInAdmin();

        $input = $page_redirect3->toArray();
        $input['sort_order'] = $inital_count + 1.5; // this should slot it in below 1 and above 2;

        $this->json('POST', route('page-redirects.update', ['id' => $page_redirect3->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Redirect Saved',
             ]);

        $page_redirect1->refresh();
        $page_redirect2->refresh();
        $page_redirect3->refresh();

        $this->assertEquals($inital_count + 1, $page_redirect1->sort_order);
        $this->assertEquals($inital_count + 3, $page_redirect2->sort_order);
        $this->assertEquals($inital_count + 2, $page_redirect3->sort_order);
    }

    public function test_a_trialing_slash_still_matches_in_a_page_redirect_url_regex(): void
    {
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $page->publish();

        $page_redirect = PageRedirect::factory()->create([
            'redirect' => $page->id,
        ]);

        $this->get($page_redirect->url_regex.'/')
             ->assertRedirect($page->full_slug);
    }

    public function test_a_sub_capture_group_can_simpy_redirect_to_a_parent(): void
    {
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $page->publish();

        $first_segment = Str::random(12);
        $page_redirect = PageRedirect::factory()->create([
            'url_regex' => '^'.$first_segment.'/(.*)$',
            'redirect' => $page->id,
        ]);

        $this->get($first_segment.'/'.Str::random())
             ->assertRedirect($page->full_slug);
    }

    public function test_a_specific_sub_page_can_redirect_but_there_can_also_be_a_catch_all_for_the_parent(): void
    {
        $page1 = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page1);
        $page1->publish();

        $page2 = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page2);
        $page2->publish();

        $first_segment = Str::random(12);
        $second_segment = Str::random(12);

        $page_redirect1 = PageRedirect::factory()->create([
            'url_regex' => '^'.$first_segment.'/'.$second_segment,
            'redirect' => $page1->id,
        ]);

        $page_redirect2 = PageRedirect::factory()->create([
            'url_regex' => '^'.$first_segment.'/(.*)$',
            'redirect' => $page2->id,
        ]);

        $this->assertTrue($page_redirect1->sort_order < $page_redirect2->sort_order);

        $this->get($first_segment.'/'.$second_segment)
             ->assertRedirect($page1->full_slug);

        $this->get($first_segment.'/'.Str::random())
             ->assertRedirect($page2->full_slug);
    }

    public function test_the_application_url_redirects(): void
    {
        $page = Page::findOrFail(56);

        $page_redirect = PageRedirect::factory()->create([
            'url_regex' => '^admissions/application-process/(.*)$',
            'redirect' => $page->id,
        ]);

        $url = 'admissions/application-process/application-process/#/?c=2409';

        $this->get($url)
             ->assertRedirect($page->full_slug);
    }
}
