<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Integration\System;

use App\Model\Route;
use App\Model\RouteGroup;
use HyperfTest\TestCase;
use HyperfTest\Traits\AuthenticatedTestTrait;

/**
 * RouteGroupController::syncRoutes 集成测试
 *
 * 覆盖正常同步、空数组清空、跨组移动、无效 route_id、404 路由组、401 未认证
 *
 * @internal
 * @coversNothing
 */
class RouteGroupSyncTest extends TestCase
{
    use AuthenticatedTestTrait;

    private static ?int $groupAId = null;

    private static ?int $groupBId = null;

    /** @var int[] */
    private static array $routeIds = [];

    /**
     * 确保测试数据：2 个路由组 + 3 条路由
     */
    protected function ensureTestData(): void
    {
        if (self::$groupAId !== null) {
            return;
        }

        [$groupAId, $groupBId, $routeIds] = $this->runInCoroutine(static function (): array {
            $groupA = RouteGroup::query()->create([
                'name' => 'sync-test-group-a',
                'label' => '同步测试组A',
                'sort_order' => 99,
            ]);

            $groupB = RouteGroup::query()->create([
                'name' => 'sync-test-group-b',
                'label' => '同步测试组B',
                'sort_order' => 99,
            ]);

            $ids = [];
            for ($i = 1; $i <= 3; $i++) {
                $route = Route::query()->create([
                    'method' => 'GET',
                    'path' => "/api/v1/sync-test/route-{$i}",
                    'name' => "sync-test-route-{$i}",
                ]);
                $ids[] = $route->id;
            }

            return [$groupA->id, $groupB->id, $ids];
        });

        self::$groupAId = $groupAId;
        self::$groupBId = $groupBId;
        self::$routeIds = $routeIds;
    }

    public static function tearDownAfterClass(): void
    {
        $groupAId = self::$groupAId;
        $groupBId = self::$groupBId;
        $routeIds = self::$routeIds;

        if ($groupAId !== null) {
            $cleanup = static function () use ($groupAId, $groupBId, $routeIds): void {
                Route::query()->whereIn('id', $routeIds)->delete();
                RouteGroup::query()->whereIn('id', [$groupAId, $groupBId])->delete();
            };

            if (\Swoole\Coroutine::getCid() > 0) {
                $cleanup();
            } else {
                \Swoole\Coroutine\run($cleanup);
            }

            self::$groupAId = null;
            self::$groupBId = null;
            self::$routeIds = [];
        }

        parent::tearDownAfterClass();
    }

    // ========== 正常同步 ==========

    public function test_sync_routes_assigns_routes_to_group(): void
    {
        $this->ensureTestData();

        $response = $this->put(
            "/api/v1/route-groups/" . self::$groupAId . "/routes",
            ['route_ids' => self::$routeIds],
            $this->authHeaders()
        );

        $response->assertOk();
        $body = $response->json();

        $this->assertSame(0, $body['code']);
        $this->assertSame('同步成功', $body['message']);
        $this->assertSame(3, $body['data']['routes_count']);

        // 验证数据库
        $this->runInCoroutine(function (): void {
            foreach (self::$routeIds as $route_id) {
                $route = Route::query()->find($route_id);
                $this->assertSame(self::$groupAId, $route->group_id);
            }
        });
    }

    // ========== 空数组清空 ==========

    public function test_sync_empty_array_clears_all_routes(): void
    {
        $this->ensureTestData();

        // 先分配路由到组A
        $this->put(
            "/api/v1/route-groups/" . self::$groupAId . "/routes",
            ['route_ids' => self::$routeIds],
            $this->authHeaders()
        );

        // 用空数组清空
        $response = $this->put(
            "/api/v1/route-groups/" . self::$groupAId . "/routes",
            ['route_ids' => []],
            $this->authHeaders()
        );

        $response->assertOk();
        $body = $response->json();

        $this->assertSame(0, $body['code']);
        $this->assertSame(0, $body['data']['routes_count']);

        // 验证数据库
        $this->runInCoroutine(function (): void {
            foreach (self::$routeIds as $route_id) {
                $route = Route::query()->find($route_id);
                $this->assertNull($route->group_id);
            }
        });
    }

    // ========== 跨组移动 ==========

    public function test_sync_moves_routes_from_another_group(): void
    {
        $this->ensureTestData();

        // 先分配路由到组A
        $this->put(
            "/api/v1/route-groups/" . self::$groupAId . "/routes",
            ['route_ids' => self::$routeIds],
            $this->authHeaders()
        );

        // 将其中两条路由同步到组B
        $moved_ids = [self::$routeIds[0], self::$routeIds[1]];
        $response = $this->put(
            "/api/v1/route-groups/" . self::$groupBId . "/routes",
            ['route_ids' => $moved_ids],
            $this->authHeaders()
        );

        $response->assertOk();
        $body = $response->json();
        $this->assertSame(2, $body['data']['routes_count']);

        // 验证：移动的路由属于组B，第三条仍属于组A
        $this->runInCoroutine(function () use ($moved_ids): void {
            foreach ($moved_ids as $route_id) {
                $route = Route::query()->find($route_id);
                $this->assertSame(self::$groupBId, $route->group_id);
            }
            $remaining = Route::query()->find(self::$routeIds[2]);
            $this->assertSame(self::$groupAId, $remaining->group_id);
        });
    }

    // ========== 无效 route_id ==========

    public function test_sync_with_invalid_route_ids_succeeds_silently(): void
    {
        $this->ensureTestData();

        $response = $this->put(
            "/api/v1/route-groups/" . self::$groupAId . "/routes",
            ['route_ids' => [999999, 999998]],
            $this->authHeaders()
        );

        $response->assertOk();
        $body = $response->json();
        $this->assertSame(0, $body['code']);
        $this->assertSame(0, $body['data']['routes_count']);
    }

    // ========== 404 路由组 ==========

    public function test_sync_nonexistent_group_returns_404(): void
    {
        $response = $this->put(
            '/api/v1/route-groups/999999/routes',
            ['route_ids' => [1]],
            $this->authHeaders()
        );

        $response->assertStatus(404);
        $body = $response->json();
        $this->assertSame(404, $body['code']);
    }

    // ========== 参数校验 ==========

    public function test_sync_without_route_ids_returns_422(): void
    {
        $this->ensureTestData();

        $response = $this->put(
            "/api/v1/route-groups/" . self::$groupAId . "/routes",
            [],
            $this->authHeaders()
        );

        $response->assertStatus(422);
        $body = $response->json();
        $this->assertSame(422, $body['code']);
    }

    // ========== 未认证 ==========

    public function test_sync_unauthenticated_returns_401(): void
    {
        $response = $this->put(
            '/api/v1/route-groups/1/routes',
            ['route_ids' => [1]],
        );

        $response->assertStatus(401);
    }
}
