<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Unit\Service;

use App\Model\Role;
use App\Model\User;
use App\Service\ScopeBitmapService;
use App\Service\ScopeTableManager;
use Hyperf\DbConnection\Db;
use PHPUnit\Framework\TestCase;

/**
 * @internal
 * @coversNothing
 */
class ScopeTableManagerTest extends TestCase
{
    protected ScopeTableManager $manager;
    protected ScopeBitmapService $bitmapService;

    protected function setUp(): void
    {
        parent::setUp();
        $this->bitmapService = new ScopeBitmapService();
        $this->manager = new ScopeTableManager($this->bitmapService);
    }

    protected 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) {
            throw $exception;
        }
    }

    public function test_table_is_created(): void
    {
        $table = $this->manager->getTable();
        $this->assertInstanceOf(\Swoole\Table::class, $table);
    }

    public function test_get_user_scope_returns_null_for_nonexistent_user(): void
    {
        $this->runInCoroutine(function (): void {
            $result = $this->manager->getUserScope(999999);
            $this->assertNull($result);
        });
    }

    public function test_rebuild_user_scope_for_administrator(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $admin_role = Role::query()->where('name', 'administrator')->first();
                $user = User::query()->where('role_id', $admin_role->id)->first();

                if (!$user) {
                    $this->markTestSkipped('需要 administrator 用户');
                }

                $result = $this->manager->rebuildUserScope($user->id);
                $this->assertNotNull($result);
                $this->assertSame('administrator', $result['role']);
                $this->assertSame('', $result['scope']);
                $this->assertGreaterThan(0, $result['version']);
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_get_user_scope_lazy_loads_on_miss(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $admin_role = Role::query()->where('name', 'administrator')->first();
                $user = User::query()->where('role_id', $admin_role->id)->first();

                if (!$user) {
                    $this->markTestSkipped('需要 administrator 用户');
                }

                // 第一次调用应触发懒加载
                $result = $this->manager->getUserScope($user->id);
                $this->assertNotNull($result);
                $this->assertSame('administrator', $result['role']);

                // 第二次调用应命中 Table
                $result2 = $this->manager->getUserScope($user->id);
                $this->assertNotNull($result2);
                $this->assertSame($result['version'], $result2['version']);
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_invalidate_user_removes_from_table(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $admin_role = Role::query()->where('name', 'administrator')->first();
                $user = User::query()->where('role_id', $admin_role->id)->first();

                if (!$user) {
                    $this->markTestSkipped('需要 administrator 用户');
                }

                // 先加载到 Table
                $this->manager->rebuildUserScope($user->id);

                // 验证存在
                $table = $this->manager->getTable();
                $this->assertNotFalse($table->get((string) $user->id));

                // 清除
                $this->manager->invalidateUser($user->id);

                // 验证已删除
                $this->assertFalse($table->get((string) $user->id));
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_rebuild_writes_correct_data_to_table(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $admin_role = Role::query()->where('name', 'administrator')->first();
                $user = User::query()->where('role_id', $admin_role->id)->first();

                if (!$user) {
                    $this->markTestSkipped('需要 administrator 用户');
                }

                $this->manager->rebuildUserScope($user->id);

                $table = $this->manager->getTable();
                $row = $table->get((string) $user->id);

                $this->assertIsArray($row);
                $this->assertArrayHasKey('role', $row);
                $this->assertArrayHasKey('scope', $row);
                $this->assertArrayHasKey('version', $row);
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_rebuild_returns_null_for_user_without_role(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                // 创建无角色用户
                $user = User::query()->create([
                    'username' => 'no_role_user_' . uniqid(),
                    'email' => 'norole_' . uniqid() . '@test.com',
                    'password' => 'password',
                    'status' => 1,
                    'role_id' => null,
                ]);

                $result = $this->manager->rebuildUserScope($user->id);
                $this->assertNull($result);
            } finally {
                Db::rollBack();
            }
        });
    }
}
