<?php

declare(strict_types=1);

namespace Tests\Feature\Account;

use App\Enums\UserRole;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

final class ProfileTest extends TestCase
{
    use RefreshDatabase;

    #[Test]
    public function the_account_endpoint_requires_authentication(): void
    {
        $this->getJson('/api/v1/account')->assertUnauthorized();
    }

    #[Test]
    public function it_returns_the_signed_in_account(): void
    {
        $user = User::factory()->create(['name' => 'Asha Rai']);

        $this->actingAs($user)->getJson('/api/v1/account')
            ->assertOk()
            ->assertJsonPath('data.id', $user->public_id)
            ->assertJsonPath('data.name', 'Asha Rai')
            ->assertJsonPath('data.is_seller', false);
    }

    #[Test]
    public function it_updates_profile_fields(): void
    {
        $user = User::factory()->create();

        $this->actingAs($user)->patchJson('/api/v1/account', [
            'name' => 'Asha B. Rai',
            'bio' => 'Domain investor since 2014.',
            'country_code' => 'NP',
            'city' => 'Kathmandu',
            'website_url' => 'https://example.com',
        ])->assertOk()->assertJsonPath('data.name', 'Asha B. Rai');

        $this->assertDatabaseHas('profiles', [
            'user_id' => $user->getKey(),
            'bio' => 'Domain investor since 2014.',
            'country_code' => 'NP',
        ]);
    }

    /**
     * A verified badge must always refer to the value that was verified, so
     * changing the number has to clear the badge.
     */
    #[Test]
    public function changing_the_phone_number_clears_its_verified_state(): void
    {
        $user = User::factory()->create([
            'phone' => '+9779800000000',
            'phone_verified_at' => now(),
        ]);

        $this->actingAs($user)->patchJson('/api/v1/account', [
            'phone' => '+9779811111111',
        ])->assertOk();

        $this->assertNull($user->fresh()->phone_verified_at);
    }

    #[Test]
    public function updating_other_fields_leaves_the_phone_badge_intact(): void
    {
        $user = User::factory()->create([
            'phone' => '+9779800000000',
            'phone_verified_at' => now(),
        ]);

        $this->actingAs($user)->patchJson('/api/v1/account', ['city' => 'Pokhara'])->assertOk();

        $this->assertNotNull($user->fresh()->phone_verified_at);
    }

    /**
     * Preferences arrive as a partial map. Sending one switch must not silently
     * reset every other switch to its default.
     */
    #[Test]
    public function notification_preferences_merge_rather_than_replace(): void
    {
        $user = User::factory()->create();

        $this->actingAs($user)->patchJson('/api/v1/account', [
            'notification_preferences' => ['email_weekly_digest' => false],
        ])->assertOk();

        $preferences = $user->fresh()->profile->preferences();

        $this->assertFalse($preferences['email_weekly_digest']);
        $this->assertTrue($preferences['email_offers'], 'An unrelated preference was reset.');
    }

    #[Test]
    public function it_rejects_an_unknown_notification_preference(): void
    {
        $user = User::factory()->create();

        $this->actingAs($user)->patchJson('/api/v1/account', [
            'notification_preferences' => ['email_something_invented' => true],
        ])->assertUnprocessable()
            ->assertJsonValidationErrors('notification_preferences.email_something_invented');
    }

    /**
     * Privilege escalation must not be reachable through a profile edit.
     */
    #[Test]
    public function it_ignores_attempts_to_change_role_or_status(): void
    {
        $user = User::factory()->create();

        $this->actingAs($user)->patchJson('/api/v1/account', [
            'name' => 'Asha Rai',
            'role' => 'super_admin',
            'status' => 'active',
            'email' => 'someone-else@example.com',
        ])->assertOk();

        $fresh = $user->fresh();

        $this->assertSame(UserRole::User, $fresh->role);
        $this->assertNotSame('someone-else@example.com', $fresh->email);
    }

    #[Test]
    public function it_rejects_a_phone_number_already_used_by_another_account(): void
    {
        User::factory()->create(['phone' => '+9779800000000']);
        $user = User::factory()->create();

        $this->actingAs($user)->patchJson('/api/v1/account', [
            'phone' => '+9779800000000',
        ])->assertUnprocessable()->assertJsonValidationErrors('phone');
    }

    #[Test]
    public function it_records_profile_changes_in_the_audit_trail(): void
    {
        $user = User::factory()->create();

        $this->actingAs($user)->patchJson('/api/v1/account', ['city' => 'Kathmandu'])->assertOk();

        $this->assertDatabaseHas('audit_logs', [
            'event' => 'user.profile_updated',
            'actor_id' => $user->getKey(),
        ]);
    }

    /**
     * A suspension must bite on the next request, not at the next sign-in.
     */
    #[Test]
    public function a_suspended_account_is_blocked_from_member_endpoints(): void
    {
        $user = User::factory()->suspended()->create();

        $this->actingAs($user)->getJson('/api/v1/account')
            ->assertForbidden()
            ->assertJsonPath('status', 'suspended');
    }

    #[Test]
    public function the_tax_id_is_not_serialised_to_the_client(): void
    {
        $user = User::factory()->create();
        $user->profile->update(['tax_id' => 'TAX-123-456']);

        $response = $this->actingAs($user)->getJson('/api/v1/account')->assertOk();

        $this->assertStringNotContainsString('TAX-123-456', $response->getContent());
    }
}
