<?php

namespace Tests\Tenant;

use Mtc\MercuryDataModels\SeoRedirect;
use Tests\TenantTestCase;
use Tests\UserForTenant;

class SeoRedirectTest extends TenantTestCase
{
    use UserForTenant;

    public function testIndex()
    {
        SeoRedirect::factory(10)->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.seo_settings.redirects.index'));

        $response->assertStatus(200);
        $this->assertEquals(10, $response->json('total'));
    }

    /**
     * Tests that a redirect can be fetched by id.
     *
     * A redirect is created using the factory, and then fetched using the show
     * route. The response is then checked to ensure that it matches the
     * expected data.
     */
    public function testShow()
    {
        $redirect = SeoRedirect::factory()->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.seo_settings.redirects.show', $redirect));

        $response->assertStatus(200);
        $this->assertEquals($redirect->id, $response->json('id'));
        $this->assertEquals($redirect->from, $response->json('from'));
    }

    public function testStore()
    {
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/source',
                'to' => '/destination',
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
                'code' => 300
            ]);

        $response->assertStatus(201);
        $this->assertEquals('/source', $response->json('from'));
        $this->assertEquals('/destination', $response->json('to'));
    }

    public function testUpdate()
    {
        $redirect = SeoRedirect::factory()->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.seo_settings.redirects.update', $redirect), [
                'from' => '/source',
                'to' => '/destination',
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
                'code' => 303
            ]);

        $response->assertStatus(200);
        $this->assertEquals('/source', $response->json('from'));
        $this->assertEquals('/destination', $response->json('to'));
        $this->assertEquals(303, $response->json('code'));
    }

    public function testDestroy()
    {
        $redirect = SeoRedirect::factory()->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.seo_settings.redirects.destroy', $redirect));

        $response->assertStatus(200);
        $this->assertFalse(SeoRedirect::query()->where('id', $redirect->id)->exists());
    }

    public function testExport()
    {
        SeoRedirect::factory(10)->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.export'));

        $response->assertStatus(200);
        $response->assertDownload();
    }

    public function testFailsIfDuplicateFromAndFromDomainCombinationExists()
    {
        // Create a valid redirect
        $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/about-us',
                'to' => '/about-company',
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ])->assertStatus(201);

        // Attempt to create a duplicate
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/about-us',
                'to' => '/about-company',
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ]);

        // Check for validation error
        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['from']);
    }

    public function testFailsIfInvalidFromDomainFormatIsUsed()
    {
        // Attempt to create a redirect with an invalid 'from_domain'
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/about-us',
                'to' => '/about-company',
                'code' => 301,
                'from_domain' => 'invalid-domain', // Invalid domain
                'to_domain' => 'https://www.example.com',
            ]);

        // Check for validation error
        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['from_domain']);
    }

    public function testFailsIfInvalidToDomainFormatIsUsed()
    {
        // Attempt to create a redirect with an invalid 'to_domain'
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/about-us',
                'to' => '/about-company',
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'invalid-domain',
            ]);

        // Check for validation error
        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['to_domain']);
    }

    public function testFailsIfInvalidSlugFormatIsUsedForFromField()
    {
        // Attempt to create a redirect with an invalid 'from' slug
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => 'invalid slug!',
                'to' => '/about-company',
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ]);

        // Check for validation error
        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['from']);
    }

    public function testFailsIfInvalidSlugFormatIsUsedForToField()
    {
        // Attempt to create a redirect with an invalid 'to' slug
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/about-us',
                'to' => 'invalid slug!', // Invalid slug format
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ]);

        // Check for validation error
        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['to']);
    }

    public function testFailsIfFromOrToFieldDoesNotStartWithForwardSlash()
    {
        // Attempt to create a redirect with a 'from' value that doesn't start with a forward slash
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => 'about-us', // Missing forward slash
                'to' => '/about-company',
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ]);

        // Check for validation error on the 'from' field
        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['from']);

        // Attempt to create a redirect with a 'to' value that doesn't start with a forward slash
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/about-us',
                'to' => 'about-company', // Missing forward slash
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ]);

        // Check for validation error on the 'to' field
        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['to']);
    }

    public function testHandlesMultiLevelPaths()
    {
        // Attempt to create a redirect with multi-level paths for 'from' and 'to'
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.seo_settings.redirects.store'), [
                'from' => '/test/test', // Multi-level path
                'to' => '/example/example', // Multi-level path
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ]);

        // Assert the request was successful
        $response->assertStatus(201);

        // Assert that the 'from' and 'to' fields match the expected values
        $this->assertEquals('/test/test', $response->json('from'));
        $this->assertEquals('/example/example', $response->json('to'));

        // Verify that the redirect was created in the database
        $this->assertDatabaseHas('seo_redirects', [
            'from' => '/test/test',
            'to' => '/example/example',
            'code' => 301,
            'from_domain' => 'https://www.example.com',
            'to_domain' => 'https://www.example.com',
        ]);
    }

    public function testHandlesVariousSpecialCharactersInPaths()
    {
        // Define a set of paths with various special characters
        $specialCharacterPaths = [
            '/page-11/used-car-results/~1/~1.aspx',
            '/test-path/!@#$%^&*()[]{}|',
            '/search-results/`~<>?:";\'',
            '/data-path/+=_,./\\-space',
            '/unicode/路径/テスト/مسار',
        ];

        foreach ($specialCharacterPaths as $index => $path) {
            // Generate a different 'to' path
            $toPath = $path . '-redirect';

            // Attempt to create a redirect with special characters in 'from' and 'to'
            $response = $this->actingAs($this->getUser())
                ->withHeader('X-Tenant', tenant('id'))
                ->postJson(route('tenant.seo_settings.redirects.store'), [
                    'from' => $path,
                    'to' => $toPath,
                    'code' => 301,
                    'from_domain' => 'https://www.example.com',
                    'to_domain' => 'https://www.example.com',
                ]);

            // Assert the request was successful
            $response->assertStatus(201);

            // Assert that the 'from' and 'to' fields match the expected values
            $this->assertEquals($path, $response->json('from'));
            $this->assertEquals($toPath, $response->json('to'));

            // Verify that the redirect was created in the database
            $this->assertDatabaseHas('seo_redirects', [
                'from' => $path,
                'to' => $toPath,
                'code' => 301,
                'from_domain' => 'https://www.example.com',
                'to_domain' => 'https://www.example.com',
            ]);
        }
    }
}
