<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Enums\AuditEvent;
use App\Models\AuditLog;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class CreateMasterUserCommandTest extends TestCase
{
    use RefreshDatabase;

    #[Test]
    public function it_creates_a_master_account_non_interactively(): void
    {
        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'priya@aumnitechworks.com',
            '--password' => 'correct-horse-battery',
        ])->assertSuccessful();

        $user = User::query()->where('email', 'priya@aumnitechworks.com')->sole();

        $this->assertTrue($user->is_master);
        $this->assertTrue($user->is_active);
        $this->assertFalse($user->must_change_password);
        $this->assertTrue(Hash::check('correct-horse-battery', $user->password));
        $this->assertNotNull($user->password_changed_at);
    }

    #[Test]
    public function a_generated_password_forces_a_change_at_first_sign_in(): void
    {
        // Whoever ran the command has seen the password, so the account holder
        // must replace it before doing anything else.
        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'priya@aumnitechworks.com',
            '--generate' => true,
        ])->assertSuccessful();

        $this->assertTrue(
            User::query()->where('email', 'priya@aumnitechworks.com')->sole()->must_change_password,
        );
    }

    #[Test]
    public function force_change_can_be_requested_explicitly(): void
    {
        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'priya@aumnitechworks.com',
            '--password' => 'correct-horse-battery',
            '--force-change' => true,
        ])->assertSuccessful();

        $this->assertTrue(
            User::query()->where('email', 'priya@aumnitechworks.com')->sole()->must_change_password,
        );
    }

    #[Test]
    public function it_rejects_a_duplicate_email(): void
    {
        User::factory()->create(['email' => 'priya@aumnitechworks.com']);

        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'priya@aumnitechworks.com',
            '--password' => 'correct-horse-battery',
        ])->assertFailed();

        $this->assertSame(1, User::query()->where('email', 'priya@aumnitechworks.com')->count());
    }

    #[Test]
    public function it_rejects_a_password_below_the_minimum_length(): void
    {
        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'priya@aumnitechworks.com',
            '--password' => 'short',
        ])->assertFailed();

        $this->assertSame(0, User::query()->count());
    }

    #[Test]
    public function it_rejects_an_invalid_email(): void
    {
        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'not-an-email',
            '--password' => 'correct-horse-battery',
        ])->assertFailed();

        $this->assertSame(0, User::query()->count());
    }

    #[Test]
    public function the_email_is_normalised_to_lower_case(): void
    {
        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'Priya@AumniTechworks.com',
            '--password' => 'correct-horse-battery',
        ])->assertSuccessful();

        $this->assertSame(1, User::query()->where('email', 'priya@aumnitechworks.com')->count());
    }

    #[Test]
    public function creating_the_account_is_audited(): void
    {
        $this->artisan('ams:create-master-user', [
            '--name' => 'Priya Sharma',
            '--email' => 'priya@aumnitechworks.com',
            '--password' => 'correct-horse-battery',
        ])->assertSuccessful();

        // Exactly one entry. The Auditable trait records the creation; the
        // command must not log it a second time.
        $log = AuditLog::query()->where('event', AuditEvent::Created->value)->sole();

        $this->assertSame('Created User Priya Sharma', $log->description);
        $this->assertSame('console', $log->url);
        $this->assertNull($log->ip_address);

        // No authenticated actor in a console context.
        $this->assertNull($log->user_id);

        $this->assertSame('priya@aumnitechworks.com', $log->new_values['email'] ?? null);
        $this->assertTrue((bool) ($log->new_values['is_master'] ?? false));
        $this->assertSame(config('ams.audit.mask'), $log->new_values['password'] ?? null);
    }

    #[Test]
    public function the_database_seeder_creates_no_login_accounts(): void
    {
        // A seeded login is a password in version control.
        $this->seed(DatabaseSeeder::class);

        $this->assertSame(0, User::query()->count());
    }
}
