<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Unit\Service;

use App\Model\Platform;
use App\Model\Store;
use App\Model\User;
use App\Model\UserDataScope;
use App\Service\ScopeBitmapService;
use Hyperf\DbConnection\Db;
use PHPUnit\Framework\TestCase;

/**
 * @internal
 * @coversNothing
 */
class ScopeBitmapServiceTest extends TestCase
{
    protected ScopeBitmapService $service;

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

    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;
        }
    }

    // --- encode/decode 双向转换 ---

    public function test_encode_empty_array_returns_empty_string(): void
    {
        $this->assertSame('', $this->service->encode([]));
    }

    public function test_encode_single_store_id(): void
    {
        $bitmap = $this->service->encode([1]);
        $this->assertSame([1], $this->service->decode($bitmap));
    }

    public function test_encode_multiple_store_ids(): void
    {
        $ids = [1, 3, 5, 8, 10];
        $bitmap = $this->service->encode($ids);
        $decoded = $this->service->decode($bitmap);
        $this->assertSame($ids, $decoded);
    }

    public function test_encode_decode_large_ids(): void
    {
        $ids = [1, 100, 500, 1000];
        $bitmap = $this->service->encode($ids);
        $decoded = $this->service->decode($bitmap);
        $this->assertSame($ids, $decoded);
    }

    public function test_decode_empty_string_returns_empty_array(): void
    {
        $this->assertSame([], $this->service->decode(''));
    }

    public function test_encode_preserves_order(): void
    {
        // 编码时乱序，解码后应升序
        $bitmap = $this->service->encode([5, 1, 3]);
        $this->assertSame([1, 3, 5], $this->service->decode($bitmap));
    }

    // --- has 检查 ---

    public function test_has_returns_true_for_existing_id(): void
    {
        $bitmap = $this->service->encode([1, 5, 10]);
        $this->assertTrue($this->service->has($bitmap, 1));
        $this->assertTrue($this->service->has($bitmap, 5));
        $this->assertTrue($this->service->has($bitmap, 10));
    }

    public function test_has_returns_false_for_missing_id(): void
    {
        $bitmap = $this->service->encode([1, 5, 10]);
        $this->assertFalse($this->service->has($bitmap, 2));
        $this->assertFalse($this->service->has($bitmap, 6));
        $this->assertFalse($this->service->has($bitmap, 11));
    }

    public function test_has_returns_false_for_id_beyond_bitmap_length(): void
    {
        $bitmap = $this->service->encode([1]);
        $this->assertFalse($this->service->has($bitmap, 100));
    }

    public function test_has_returns_false_for_empty_bitmap(): void
    {
        $this->assertFalse($this->service->has('', 1));
    }

    // --- merge 合并 ---

    public function test_merge_two_bitmaps(): void
    {
        $bitmap1 = $this->service->encode([1, 3]);
        $bitmap2 = $this->service->encode([2, 4]);
        $merged = $this->service->merge($bitmap1, $bitmap2);

        $this->assertSame([1, 2, 3, 4], $this->service->decode($merged));
    }

    public function test_merge_overlapping_bitmaps(): void
    {
        $bitmap1 = $this->service->encode([1, 3, 5]);
        $bitmap2 = $this->service->encode([3, 5, 7]);
        $merged = $this->service->merge($bitmap1, $bitmap2);

        $this->assertSame([1, 3, 5, 7], $this->service->decode($merged));
    }

    public function test_merge_different_length_bitmaps(): void
    {
        $bitmap1 = $this->service->encode([1]);
        $bitmap2 = $this->service->encode([100]);
        $merged = $this->service->merge($bitmap1, $bitmap2);

        $this->assertTrue($this->service->has($merged, 1));
        $this->assertTrue($this->service->has($merged, 100));
    }

    public function test_merge_with_empty_bitmaps(): void
    {
        $bitmap = $this->service->encode([1, 3]);
        $merged = $this->service->merge($bitmap, '');
        $this->assertSame([1, 3], $this->service->decode($merged));
    }

    public function test_merge_all_empty_returns_empty(): void
    {
        $this->assertSame('', $this->service->merge('', ''));
    }

    // --- buildForUser（需要数据库） ---

    public function test_build_for_user_with_store_scope(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $user = User::query()->first();

                // 查已有 store
                $stores = Store::query()->limit(2)->pluck('id')->toArray();
                if (count($stores) < 2) {
                    $this->markTestSkipped('需要至少 2 个 store 记录');
                }

                // 插入 store 类型的 scope
                foreach ($stores as $store_id) {
                    UserDataScope::query()->create([
                        'user_id' => $user->id,
                        'scope_type' => 'store',
                        'scope_id' => $store_id,
                    ]);
                }

                $bitmap = $this->service->buildForUser($user->id);
                $this->assertNotEmpty($bitmap);

                foreach ($stores as $store_id) {
                    $this->assertTrue($this->service->has($bitmap, $store_id));
                }
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_build_for_user_with_company_scope(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $user = User::query()->first();

                // 查找有 store 的 company
                $store = Store::query()->first();
                if (!$store) {
                    $this->markTestSkipped('需要至少 1 个 store 记录');
                }

                UserDataScope::query()->create([
                    'user_id' => $user->id,
                    'scope_type' => 'company',
                    'scope_id' => $store->company_id,
                ]);

                $bitmap = $this->service->buildForUser($user->id);
                $this->assertNotEmpty($bitmap);

                // 该 company 下的 store 应在 bitmap 中
                $company_stores = Store::query()
                    ->where('company_id', $store->company_id)
                    ->pluck('id')
                    ->toArray();

                foreach ($company_stores as $sid) {
                    $this->assertTrue($this->service->has($bitmap, $sid));
                }
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_build_for_user_with_no_scopes_returns_empty(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $user = User::query()->first();
                // 不插入 scope
                $bitmap = $this->service->buildForUser($user->id);
                $this->assertSame('', $bitmap);
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_build_for_user_with_mixed_scopes(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $user = User::query()->first();
                $store = Store::query()->first();
                if (!$store) {
                    $this->markTestSkipped('需要至少 1 个 store 记录');
                }

                // 同时添加 company 和 store 类型
                UserDataScope::query()->create([
                    'user_id' => $user->id,
                    'scope_type' => 'company',
                    'scope_id' => $store->company_id,
                ]);

                // 找一个不属于该 company 的 store
                $other_store = Store::query()
                    ->where('company_id', '!=', $store->company_id)
                    ->first();
                if ($other_store) {
                    UserDataScope::query()->create([
                        'user_id' => $user->id,
                        'scope_type' => 'store',
                        'scope_id' => $other_store->id,
                    ]);
                }

                $bitmap = $this->service->buildForUser($user->id);
                $this->assertNotEmpty($bitmap);
                // company 下的 store 在 bitmap 中
                $this->assertTrue($this->service->has($bitmap, $store->id));
                // 额外的 store 也在
                if ($other_store) {
                    $this->assertTrue($this->service->has($bitmap, $other_store->id));
                }
            } finally {
                Db::rollBack();
            }
        });
    }

    // --- buildForDeveloper ---

    public function test_build_for_developer_returns_platform_stores(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                $user = User::query()->first();

                // 查找有 store 的 platform
                $store = Store::query()->first();
                if (!$store) {
                    $this->markTestSkipped('需要至少 1 个 store 记录');
                }

                // 将 platform 绑定到用户
                Platform::query()
                    ->where('id', $store->platform_id)
                    ->update(['developer_id' => $user->id]);

                $bitmap = $this->service->buildForDeveloper($user->id);
                $this->assertNotEmpty($bitmap);

                $platform_stores = Store::query()
                    ->where('platform_id', $store->platform_id)
                    ->pluck('id')
                    ->toArray();

                foreach ($platform_stores as $sid) {
                    $this->assertTrue($this->service->has($bitmap, $sid));
                }
            } finally {
                Db::rollBack();
            }
        });
    }

    public function test_build_for_developer_no_platforms_returns_empty(): void
    {
        $this->runInCoroutine(function (): void {
            Db::beginTransaction();
            try {
                // 创建一个不维护任何平台的用户
                $user = User::query()->create([
                    'username' => 'dev_no_platform_' . uniqid(),
                    'email' => 'devnp_' . uniqid() . '@test.com',
                    'password' => 'password',
                    'status' => 1,
                ]);

                $bitmap = $this->service->buildForDeveloper($user->id);
                $this->assertSame('', $bitmap);
            } finally {
                Db::rollBack();
            }
        });
    }
}
