<?php

declare(strict_types=1);

namespace Tests\Feature\Pwa;

use App\Models\Client;
use App\Models\Company;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Event;
use Laravel\Sanctum\Sanctum;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

class UpdateClientTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private User $admin;

    private User $collector;

    protected function setUp(): void
    {
        parent::setUp();
        Event::fake();

        foreach (['admin', 'supervisor', 'collector'] as $role) {
            Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']);
        }

        $plan = Plan::factory()->create(['has_pwa_access' => true]);
        $this->company = Company::factory()->create();
        Subscription::factory()->active()->create([
            'company_id' => $this->company->id,
            'plan_id' => $plan->id,
            'ends_at' => now()->addYear(),
        ]);

        $this->admin = User::factory()->create(['company_id' => $this->company->id]);
        $this->admin->assignRole('admin');
        $this->admin = $this->admin->fresh();

        $this->collector = User::factory()->create(['company_id' => $this->company->id]);
        $this->collector->assignRole('collector');
        $this->collector = $this->collector->fresh();
    }

    private function makeClient(?int $createdBy = null): Client
    {
        return Client::factory()->create([
            'company_id' => $this->company->id,
            'created_by_user_id' => $createdBy ?? $this->admin->id,
            'name' => 'Original Name',
            'identification' => '1000',
            'phone' => '3000000000',
            'email' => null,
            'is_active' => true,
        ]);
    }

    #[Test]
    public function show_returns_structured_address_fields_for_prefill(): void
    {
        $client = $this->makeClient();
        $client->addresses()->create([
            'is_primary' => true,
            'state' => 'Antioquia',
            'city' => 'Medellin',
            'address_line_1' => 'Cra 10 #20-30',
        ]);

        Sanctum::actingAs($this->admin);

        $this->getJson("/api/pwa/clients/{$client->id}")
            ->assertStatus(200)
            ->assertJsonPath('state', 'Antioquia')
            ->assertJsonPath('city', 'Medellin')
            ->assertJsonPath('address_line_1', 'Cra 10 #20-30');
    }

    #[Test]
    public function an_admin_can_update_a_client_and_upsert_its_primary_address(): void
    {
        $client = $this->makeClient();
        Sanctum::actingAs($this->admin);

        $this->putJson("/api/pwa/clients/{$client->id}", [
            'name' => 'Nuevo Nombre',
            'identification' => '2000',
            'phone' => '3111111111',
            'email' => 'nuevo@mail.com',
            'state' => 'Santander',
            'city' => 'Bucaramanga',
            'address' => 'Calle 1 #2-3',
        ])->assertStatus(200)
            ->assertJsonPath('client.name', 'Nuevo Nombre')
            ->assertJsonPath('client.address', 'Calle 1 #2-3, Bucaramanga, Santander');

        $this->assertDatabaseHas('clients', [
            'id' => $client->id,
            'name' => 'Nuevo Nombre',
            'identification' => '2000',
            'phone' => '3111111111',
            'email' => 'nuevo@mail.com',
        ]);
        $this->assertDatabaseHas('client_addresses', [
            'client_id' => $client->id,
            'is_primary' => true,
            'state' => 'Santander',
            'city' => 'Bucaramanga',
            'address_line_1' => 'Calle 1 #2-3',
        ]);
    }

    #[Test]
    public function a_collector_can_update_a_client_they_created(): void
    {
        $client = $this->makeClient(createdBy: $this->collector->id);
        Sanctum::actingAs($this->collector);

        $this->putJson("/api/pwa/clients/{$client->id}", [
            'name' => 'Editado Cobrador',
            'identification' => '1000',
            'phone' => '3000000000',
        ])->assertStatus(200);

        $this->assertDatabaseHas('clients', ['id' => $client->id, 'name' => 'Editado Cobrador']);
    }

    #[Test]
    public function a_collector_cannot_update_a_client_outside_their_scope(): void
    {
        $client = $this->makeClient(createdBy: $this->admin->id);
        Sanctum::actingAs($this->collector);

        $this->putJson("/api/pwa/clients/{$client->id}", [
            'name' => 'X',
            'identification' => '1000',
            'phone' => '3000000000',
        ])->assertStatus(404);

        $this->assertDatabaseHas('clients', ['id' => $client->id, 'name' => 'Original Name']);
    }

    #[Test]
    public function a_client_from_another_company_cannot_be_updated(): void
    {
        $otherCompany = Company::factory()->create();
        $foreign = Client::factory()->create([
            'company_id' => $otherCompany->id,
            'created_by_user_id' => $this->admin->id,
            'identification' => '9999',
        ]);
        Sanctum::actingAs($this->admin);

        $this->putJson("/api/pwa/clients/{$foreign->id}", [
            'name' => 'Hack',
            'identification' => '9999',
            'phone' => '3000000000',
        ])->assertStatus(404);
    }

    #[Test]
    public function keeping_own_identification_is_allowed_but_duplicating_another_is_rejected(): void
    {
        $client = $this->makeClient(); // identification 1000
        Client::factory()->create([
            'company_id' => $this->company->id,
            'created_by_user_id' => $this->admin->id,
            'identification' => '5000',
        ]);
        Sanctum::actingAs($this->admin);

        $this->putJson("/api/pwa/clients/{$client->id}", [
            'name' => 'Same Ced', 'identification' => '1000', 'phone' => '3000000000',
        ])->assertStatus(200);

        $this->putJson("/api/pwa/clients/{$client->id}", [
            'name' => 'Dup', 'identification' => '5000', 'phone' => '3000000000',
        ])->assertStatus(422);
    }
}
