<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Unit\Command;

use App\Command\UserResetPasswordCommand;
use App\Model\Role;
use App\Model\User;
use Hyperf\Context\ApplicationContext;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;

/**
 * UserResetPasswordCommand 单元测试
 *
 * @internal
 * @coversNothing
 */
class UserResetPasswordCommandTest extends TestCase
{
    private ?int $testUserId = null;

    private function runInCoroutine(callable $callback): void
    {
        if (\Swoole\Coroutine::getCid() > 0) {
            $callback();
            return;
        }

        $exception = null;
        \Swoole\Coroutine\run(static function () use ($callback, &$exception): void {
            try {
                $callback();
            } catch (\Throwable $e) {
                $exception = $e;
            }
        });
        if ($exception !== null) {
            throw $exception;
        }
    }

    protected function tearDown(): void
    {
        if ($this->testUserId !== null) {
            $this->runInCoroutine(function (): void {
                User::query()->where('id', $this->testUserId)->delete();
            });
            $this->testUserId = null;
        }
        parent::tearDown();
    }

    private function createTestUser(): User
    {
        $role = Role::query()->first();
        $unique = 'test_reset_' . uniqid();
        $user = User::create([
            'username' => $unique,
            'email' => $unique . '@test.com',
            'password' => 'old_password_123',
            'status' => 1,
            'role_id' => $role?->id,
            'ext' => [],
        ]);
        $this->testUserId = $user->id;

        return $user;
    }

    public function test_reset_password_by_username(): void
    {
        $this->runInCoroutine(function (): void {
            $test_user = $this->createTestUser();

            $container = ApplicationContext::getContainer();
            $command = new UserResetPasswordCommand($container);
            $tester = new CommandTester($command);
            $tester->execute([
                'identifier' => $test_user->username,
                '--password' => 'new_password_123',
            ]);

            $this->assertSame(0, $tester->getStatusCode());
            $this->assertStringContainsString('has been reset', $tester->getDisplay());

            // 验证密码确实已更新
            $updated_user = User::find($test_user->id);
            $this->assertTrue($updated_user->verifyPassword('new_password_123'));
        });
    }

    public function test_reset_password_by_id(): void
    {
        $this->runInCoroutine(function (): void {
            $test_user = $this->createTestUser();

            $container = ApplicationContext::getContainer();
            $command = new UserResetPasswordCommand($container);
            $tester = new CommandTester($command);
            $tester->execute([
                'identifier' => (string) $test_user->id,
                '--password' => 'another_pwd_456',
            ]);

            $this->assertSame(0, $tester->getStatusCode());
            $this->assertStringContainsString('has been reset', $tester->getDisplay());

            $updated_user = User::find($test_user->id);
            $this->assertTrue($updated_user->verifyPassword('another_pwd_456'));
        });
    }

    public function test_user_not_found_shows_error(): void
    {
        $this->runInCoroutine(function (): void {
            $container = ApplicationContext::getContainer();
            $command = new UserResetPasswordCommand($container);
            $tester = new CommandTester($command);
            $tester->execute([
                'identifier' => 'nonexistent_user_xyz_999',
                '--password' => 'some_password',
            ]);

            $this->assertSame(1, $tester->getStatusCode());
            $this->assertStringContainsString('not found', $tester->getDisplay());
        });
    }

    public function test_password_too_short_shows_error(): void
    {
        $this->runInCoroutine(function (): void {
            $test_user = $this->createTestUser();

            $container = ApplicationContext::getContainer();
            $command = new UserResetPasswordCommand($container);
            $tester = new CommandTester($command);
            $tester->execute([
                'identifier' => $test_user->username,
                '--password' => '12345',
            ]);

            $this->assertSame(1, $tester->getStatusCode());
            $this->assertStringContainsString('at least 6', $tester->getDisplay());
        });
    }
}
