<?php

namespace Tests\Feature\PublicSite;

use App\Domain\Identity\Models\User;
use App\Domain\Organization\Models\Branch;
use App\Domain\Organization\Models\Company;
use App\Domain\Website\Models\DestinationCountry;
use App\Domain\Website\Models\SiteContent;
use App\Domain\Website\Models\SiteStatistic;
use App\Domain\Website\Models\SuccessStory;
use Database\Seeders\PermissionSeeder;
use Database\Seeders\RoleSeeder;
use Database\Seeders\WebsiteSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class CmsAdminTest extends TestCase
{
    use RefreshDatabase;

    private Company $company;

    protected function setUp(): void
    {
        parent::setUp();
        Cache::flush();
        $this->seed([PermissionSeeder::class, RoleSeeder::class]);

        $this->company = Company::factory()->create();
        Branch::factory()->for($this->company)->create(['is_head_office' => true]);
        $this->seed(WebsiteSeeder::class);
    }

    private function userWithRole(string $role): User
    {
        return tap(User::factory()->create(['company_id' => $this->company->id]))->assignRole($role);
    }

    public function test_content_blocks_are_listed_grouped_for_the_editor(): void
    {
        $response = $this->actingAs($this->userWithRole('recruitment-manager'))
            ->getJson('/api/v1/cms/content')
            ->assertOk();

        $this->assertArrayHasKey('hero', $response->json('data'));
        $this->assertNotEmpty($response->json('data.hero'));
    }

    public function test_editing_a_content_block_changes_the_public_site(): void
    {
        $manager = $this->userWithRole('recruitment-manager');

        $headline = SiteContent::where('key', 'headline')->firstOrFail();

        $this->actingAs($manager)->putJson('/api/v1/cms/content', [
            'blocks' => [
                ['id' => $headline->uuid, 'value' => 'Skilled Workers, Delivered On Time'],
            ],
        ])->assertOk()->assertJsonPath('data.updated', 1);

        // The public endpoint is cached, so the save must have flushed it.
        $this->getJson('/api/v1/site/home')
            ->assertOk()
            ->assertJsonPath('data.hero.headline', 'Skilled Workers, Delivered On Time');
    }

    public function test_a_json_content_block_must_stay_parseable(): void
    {
        $steps = SiteContent::where('key', 'steps')->firstOrFail();

        $this->actingAs($this->userWithRole('recruitment-manager'))
            ->putJson('/api/v1/cms/content', [
                'blocks' => [['id' => $steps->uuid, 'value' => '{not valid json']],
            ])
            ->assertStatus(422);

        $this->assertNotSame('{not valid json', $steps->fresh()->value);
    }

    public function test_statistics_can_be_edited(): void
    {
        $statistic = SiteStatistic::where('key', 'workers_deployed')->firstOrFail();

        $this->actingAs($this->userWithRole('recruitment-manager'))
            ->putJson('/api/v1/cms/statistics', [
                'statistics' => [[
                    'id' => $statistic->uuid,
                    'label' => 'Workers Deployed',
                    'value' => '14,500',
                    'suffix' => '+',
                ]],
            ])
            ->assertOk();

        $this->getJson('/api/v1/site/home')
            ->assertOk()
            ->assertJsonFragment(['label' => 'Workers Deployed', 'value' => '14,500']);
    }

    public function test_a_country_can_be_created_and_unpublished(): void
    {
        $manager = $this->userWithRole('recruitment-manager');

        $id = $this->actingAs($manager)->postJson('/api/v1/cms/countries', [
            'code' => 'bh',
            'name' => 'Bahrain',
            'summary' => 'Placements in construction and hospitality.',
            'is_published' => true,
        ])->assertCreated()->json('data.id');

        // The code is normalised and the slug derived.
        $country = DestinationCountry::where('uuid', $id)->firstOrFail();
        $this->assertSame('BH', $country->code);
        $this->assertSame('bahrain', $country->slug);

        $this->getJson('/api/v1/site/countries')->assertOk()->assertJsonFragment(['name' => 'Bahrain']);

        $this->actingAs($manager)
            ->patchJson("/api/v1/cms/countries/{$id}", ['is_published' => false])
            ->assertOk();

        $content = $this->getJson('/api/v1/site/countries')->assertOk()->getContent();
        $this->assertStringNotContainsString('Bahrain', $content);
    }

    public function test_the_slug_does_not_change_when_a_name_is_edited(): void
    {
        $manager = $this->userWithRole('recruitment-manager');

        $id = $this->actingAs($manager)->postJson('/api/v1/cms/countries', [
            'code' => 'lb', 'name' => 'Lebanon',
        ])->assertCreated()->json('data.id');

        $this->actingAs($manager)
            ->patchJson("/api/v1/cms/countries/{$id}", ['name' => 'Republic of Lebanon'])
            ->assertOk()
            // A shared link must keep working.
            ->assertJsonPath('data.slug', 'lebanon')
            ->assertJsonPath('data.name', 'Republic of Lebanon');
    }

    public function test_a_duplicate_country_is_a_validation_error_not_a_crash(): void
    {
        // Oman is already seeded; re-adding it must explain itself.
        $this->actingAs($this->userWithRole('recruitment-manager'))
            ->postJson('/api/v1/cms/countries', ['code' => 'OM', 'name' => 'Oman'])
            ->assertStatus(422)
            ->assertJsonStructure(['errors' => ['code']]);
    }

    public function test_a_repeated_name_still_gets_a_usable_slug(): void
    {
        $manager = $this->userWithRole('recruitment-manager');

        $first = $this->actingAs($manager)->postJson('/api/v1/cms/categories', [
            'name' => 'Welding',
        ])->assertCreated();

        $second = $this->actingAs($manager)->postJson('/api/v1/cms/categories', [
            'name' => 'Welding',
        ])->assertCreated();

        $this->assertSame('welding', $first->json('data.slug'));
        $this->assertSame('welding-2', $second->json('data.slug'));
    }

    public function test_a_story_is_unpublished_until_someone_publishes_it(): void
    {
        $manager = $this->userWithRole('recruitment-manager');

        $id = $this->actingAs($manager)->postJson('/api/v1/cms/stories', [
            'title' => 'From Dhaka to Doha',
            'worker_name' => 'Jashim Uddin',
            'occupation' => 'Welder',
            'country' => 'Qatar',
            'excerpt' => 'Three years on, he is a supervisor.',
        ])->assertCreated()->json('data.id');

        $this->assertFalse(SuccessStory::where('uuid', $id)->firstOrFail()->is_published);
        $this->getJson('/api/v1/site/stories')->assertOk()->assertJsonCount(0, 'data');

        $this->actingAs($manager)
            ->postJson("/api/v1/cms/stories/{$id}/publish", ['publish' => true])
            ->assertOk();

        $this->getJson('/api/v1/site/stories')
            ->assertOk()
            ->assertJsonCount(1, 'data')
            ->assertJsonPath('data.0.title', 'From Dhaka to Doha');
    }

    public function test_publishing_a_story_needs_the_publish_permission(): void
    {
        $manager = $this->userWithRole('recruitment-manager');
        $id = $this->actingAs($manager)->postJson('/api/v1/cms/stories', [
            'title' => 'A Story',
        ])->assertCreated()->json('data.id');

        // Branch managers hold no CMS permissions at all.
        $this->actingAs($this->userWithRole('branch-manager'))
            ->postJson("/api/v1/cms/stories/{$id}/publish")
            ->assertStatus(403);
    }

    public function test_faqs_can_be_managed(): void
    {
        $manager = $this->userWithRole('recruitment-manager');

        $id = $this->actingAs($manager)->postJson('/api/v1/cms/faqs', [
            'question' => 'Do you charge candidates a registration fee?',
            'answer' => 'No. We never ask candidates for payment to register.',
            'audience' => 'candidate',
        ])->assertCreated()->json('data.id');

        $this->getJson('/api/v1/site/faqs?audience=candidate')
            ->assertOk()
            ->assertJsonFragment(['question' => 'Do you charge candidates a registration fee?']);

        $this->actingAs($manager)->deleteJson("/api/v1/cms/faqs/{$id}")->assertOk();

        $content = $this->getJson('/api/v1/site/faqs?audience=candidate')->getContent();
        $this->assertStringNotContainsString('registration fee', $content);
    }

    public function test_cms_editing_requires_the_manage_permission(): void
    {
        // Auditors may read but must not change the public site.
        $this->actingAs($this->userWithRole('auditor'))
            ->postJson('/api/v1/cms/countries', ['code' => 'xx', 'name' => 'Nowhere'])
            ->assertStatus(403);

        $this->actingAs($this->userWithRole('auditor'))
            ->getJson('/api/v1/cms/content')
            ->assertStatus(403);
    }

    public function test_cms_records_from_another_company_are_not_reachable(): void
    {
        $foreign = DestinationCountry::factory()->create(); // another company

        $this->actingAs($this->userWithRole('recruitment-manager'))
            ->patchJson("/api/v1/cms/countries/{$foreign->uuid}", ['name' => 'Hijacked'])
            ->assertStatus(404);

        $this->assertNotSame('Hijacked', $foreign->fresh()->name);
    }

    public function test_contact_enquiries_reach_the_inbox_and_can_be_handled(): void
    {
        $this->postJson('/api/v1/site/enquiries', [
            'name' => 'Rashida Begum',
            'email' => 'rashida@example.test',
            'message' => 'Do you recruit nurses for the Gulf?',
            'audience' => 'candidate',
        ])->assertCreated();

        $officer = $this->userWithRole('support-officer');

        $id = $this->actingAs($officer)
            ->getJson('/api/v1/contact-enquiries')
            ->assertOk()
            ->assertJsonPath('data.0.name', 'Rashida Begum')
            ->json('data.0.id');

        $this->actingAs($officer)
            ->patchJson("/api/v1/contact-enquiries/{$id}", [
                'status' => 'answered',
                'internal_notes' => 'Replied by email with the nursing corridor details.',
            ])
            ->assertOk();

        $this->assertDatabaseHas('contact_enquiries', ['status' => 'answered']);
    }

    public function test_the_enquiry_inbox_is_not_public(): void
    {
        $this->getJson('/api/v1/contact-enquiries')->assertStatus(401);

        $this->actingAs($this->userWithRole('payroll-officer'))
            ->getJson('/api/v1/contact-enquiries')
            ->assertStatus(403);
    }
}
