<?php

namespace Tests\Feature;

use App\FieldVisibility\FieldCatalogueRegistry;
use App\FieldVisibility\VehicleFieldCatalogue;
use Mtc\MercuryDataModels\TenantHiddenField;
use Mtc\MercuryDataModels\User;
use Tests\DatabaseTestCase;
use Tests\UserForTenant;

class FieldVisibilityControllerTest extends DatabaseTestCase
{
    use UserForTenant;

    private const ENTITIES = ['vehicle', 'offer', 'new-car', 'dealership'];

    private function vehicleCatalogue(): VehicleFieldCatalogue
    {
        return app(FieldCatalogueRegistry::class)->for(VehicleFieldCatalogue::ENTITY);
    }

    public function testShowReturnsCatalogueForEveryEntity(): void
    {
        foreach (self::ENTITIES as $entity) {
            $catalogue = app(FieldCatalogueRegistry::class)->for($entity);

            $response = $this->actingAs($this->getUser())
                ->withHeader('X-Tenant', tenant('id'))
                ->getJson(route('tenant.settings.field-visibility.show', ['entity' => $entity]));

            $response->assertStatus(200);
            $response->assertJsonPath('entity', $entity);
            $response->assertJsonPath('total', count($catalogue->fields()));
            $response->assertJsonPath('visible', count($catalogue->fields()));
        }
    }

    public function testUpdatePersistsHideableKeyForEveryEntity(): void
    {
        foreach (self::ENTITIES as $entity) {
            $key = app(FieldCatalogueRegistry::class)->for($entity)->hideableKeys()[0];

            $this->actingAs($this->getUser())
                ->withHeader('X-Tenant', tenant('id'))
                ->putJson(route('tenant.settings.field-visibility.update', ['entity' => $entity]), [
                    'hidden' => [$key],
                ])
                ->assertStatus(200)
                ->assertJsonPath('hidden', [$key]);

            $this->assertTrue(
                TenantHiddenField::query()->forEntity($entity)->where('field_key', $key)->exists()
            );
        }
    }

    public function testUpdateRejectsLockableKeyForEveryEntity(): void
    {
        foreach (self::ENTITIES as $entity) {
            $lockable = app(FieldCatalogueRegistry::class)->for($entity)->lockableKeys();

            $this->actingAs($this->getUser())
                ->withHeader('X-Tenant', tenant('id'))
                ->putJson(route('tenant.settings.field-visibility.update', ['entity' => $entity]), [
                    'hidden' => [$lockable[0]],
                ])
                ->assertStatus(422)
                ->assertJsonValidationErrors('hidden.0');
        }
    }

    public function testShowReturnsCatalogue(): void
    {
        $catalogue = $this->vehicleCatalogue();

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.settings.field-visibility.show', ['entity' => 'vehicle']));

        $response->assertStatus(200);
        $response->assertJsonPath('entity', 'vehicle');
        $response->assertJsonPath('total', count($catalogue->fields()));
        $response->assertJsonPath('visible', count($catalogue->fields()));
        $response->assertJsonPath('hidden', []);
        $this->assertNotEmpty($response->json('groups'));
        $this->assertNotEmpty($response->json('fields'));
    }

    public function testShowReflectsHiddenFields(): void
    {
        TenantHiddenField::query()->create(['entity' => 'vehicle', 'field_key' => 'seats']);
        TenantHiddenField::query()->create(['entity' => 'vehicle', 'field_key' => 'cap_id']);

        $total = count($this->vehicleCatalogue()->fields());

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.settings.field-visibility.show', ['entity' => 'vehicle']));

        $response->assertStatus(200);
        $response->assertJsonPath('visible', $total - 2);
        $this->assertEqualsCanonicalizing(['seats', 'cap_id'], $response->json('hidden'));
    }

    public function testUpdatePersistsAndSyncsHiddenSet(): void
    {
        $user = $this->getUser();

        $this->actingAs($user)
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.settings.field-visibility.update', ['entity' => 'vehicle']), [
                'hidden' => ['seats', 'cap_id', 'rrp_price'],
            ])
            ->assertStatus(200)
            ->assertJsonPath('visible', count($this->vehicleCatalogue()->fields()) - 3);

        $this->assertTrue(
            TenantHiddenField::query()->forEntity('vehicle')->where('field_key', 'seats')->exists()
        );

        // A second save reconciles the set: rrp_price/cap_id removed, original_price added.
        $this->actingAs($user)
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.settings.field-visibility.update', ['entity' => 'vehicle']), [
                'hidden' => ['seats', 'original_price'],
            ])
            ->assertStatus(200);

        $remaining = TenantHiddenField::query()->forEntity('vehicle')->pluck('field_key')->all();
        $this->assertEqualsCanonicalizing(['seats', 'original_price'], $remaining);
    }

    public function testUpdateRejectsLockableKeys(): void
    {
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.settings.field-visibility.update', ['entity' => 'vehicle']), [
                'hidden' => ['title'],
            ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors('hidden.0');
        $this->assertSame(0, TenantHiddenField::query()->forEntity('vehicle')->count());
    }

    public function testUpdateRejectsUnknownKeys(): void
    {
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.settings.field-visibility.update', ['entity' => 'vehicle']), [
                'hidden' => ['not_a_real_field'],
            ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors('hidden.0');
    }

    public function testUnknownEntityReturnsNotFound(): void
    {
        $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.settings.field-visibility.show', ['entity' => 'vehicle']))
            ->assertStatus(200);

        // Entities without a catalogue are not routable (route is constrained to known slugs).
        $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson('api/settings/field-visibility/spaceship')
            ->assertStatus(404);
    }

    public function testRequiresAuthentication(): void
    {
        $this->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.settings.field-visibility.show', ['entity' => 'vehicle']))
            ->assertStatus(401);
    }

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

        $this->actingAs($user)
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.settings.field-visibility.update', ['entity' => 'vehicle']), [
                'hidden' => ['seats'],
            ])
            ->assertStatus(403);
    }
}
