<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Models\Paper;
use App\Models\Permission;
use App\Models\QuestionBank;
use App\Models\User;
use App\Services\QuestionImportService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use Tymon\JWTAuth\Facades\JWTAuth;

final class AdminPermissionTest extends TestCase
{
    use RefreshDatabase;

    protected bool $seed = true;

    public function test_user_without_admin_permission_cannot_access_admin_resources(): void
    {
        $user = User::factory()->create(['role' => 'user']);

        $this->withToken(JWTAuth::fromUser($user))
            ->getJson('/api/admin/banks')
            ->assertForbidden()
            ->assertJsonPath('message', '权限不足');
    }

    public function test_teacher_only_lists_owned_questions(): void
    {
        $owner = User::factory()->create(['role' => 'teacher']);
        $otherTeacher = User::factory()->create(['role' => 'teacher']);
        $ownedBank = $this->bankFor($owner, '自己的题库');
        $otherBank = $this->bankFor($otherTeacher, '别人的题库');

        app(QuestionImportService::class)->importJsonText($ownedBank, $owner, file_get_contents(base_path('question.json')));
        app(QuestionImportService::class)->importJsonText($otherBank, $otherTeacher, file_get_contents(base_path('question.json')));

        $response = $this->withToken(JWTAuth::fromUser($owner))
            ->getJson('/api/admin/questions')
            ->assertOk();

        $bankIds = collect($response->json('data.items'))->pluck('question_bank_id')->unique()->values()->all();

        $this->assertSame([$ownedBank->id], $bankIds);
    }

    public function test_teacher_cannot_import_into_another_teachers_bank(): void
    {
        $owner = User::factory()->create(['role' => 'teacher']);
        $otherTeacher = User::factory()->create(['role' => 'teacher']);
        $otherBank = $this->bankFor($otherTeacher, '别人的题库');

        $this->withToken(JWTAuth::fromUser($owner))
            ->postJson('/api/admin/questions', [
                'question_bank_id' => $otherBank->id,
                'content' => '不能越权新增',
                'type' => 'single',
                'options' => [
                    ['text' => 'A', 'correct' => true],
                    ['text' => 'B', 'correct' => false],
                ],
            ])
            ->assertForbidden();
    }

    public function test_teacher_without_user_permission_cannot_manage_users(): void
    {
        $teacher = User::factory()->create(['role' => 'teacher']);

        $this->withToken(JWTAuth::fromUser($teacher))
            ->getJson('/api/admin/users')
            ->assertForbidden()
            ->assertJsonPath('message', '权限不足');
    }

    public function test_admin_can_update_user_status_and_password(): void
    {
        $admin = User::query()->where('role', 'admin')->firstOrFail();
        $target = User::factory()->create(['role' => 'user', 'is_active' => true]);

        $this->withToken(JWTAuth::fromUser($admin))
            ->putJson("/api/admin/users/{$target->id}", [
                'name' => '新姓名',
                'role' => 'teacher',
                'is_active' => false,
                'password' => 'newpass123',
            ])
            ->assertOk()
            ->assertJsonPath('data.name', '新姓名')
            ->assertJsonPath('data.role', 'teacher')
            ->assertJsonPath('data.is_active', false);

        $this->assertDatabaseHas('users', [
            'id' => $target->id,
            'name' => '新姓名',
            'role' => 'teacher',
            'is_active' => false,
        ]);
    }

    public function test_admin_can_read_and_sync_role_permissions(): void
    {
        $admin = User::query()->where('role', 'admin')->firstOrFail();
        $permission = Permission::query()->where('code', 'banks')->firstOrFail();
        $token = JWTAuth::fromUser($admin);

        $this->withToken($token)
            ->getJson('/api/admin/permissions')
            ->assertOk()
            ->assertJsonStructure(['data' => ['permissions', 'role_permissions']]);

        $this->withToken($token)
            ->putJson('/api/admin/roles/user/permissions', [
                'permission_ids' => [$permission->id],
            ])
            ->assertOk();

        $this->assertDatabaseHas('role_permissions', [
            'role' => 'user',
            'permission_id' => $permission->id,
        ]);
        $this->assertDatabaseHas('operation_logs', [
            'action' => 'role.permissions_updated',
            'target_type' => 'role',
        ]);
    }

    public function test_teacher_can_view_and_update_owned_paper_questions(): void
    {
        $teacher = User::factory()->create(['role' => 'teacher']);
        $bank = $this->bankFor($teacher, '试卷题库');
        app(QuestionImportService::class)->importJsonText($bank, $teacher, file_get_contents(base_path('question.json')));
        $questions = $bank->questions()->take(2)->get();
        $paper = Paper::create([
            'owner_id' => $teacher->id,
            'question_bank_id' => $bank->id,
            'title' => '原试卷',
            'is_active' => true,
        ]);
        $paper->questions()->attach($questions[0]->id, ['score' => 3, 'sort' => 0]);

        $token = JWTAuth::fromUser($teacher);

        $this->withToken($token)
            ->getJson("/api/admin/papers/{$paper->id}")
            ->assertOk()
            ->assertJsonPath('data.questions.0.id', $questions[0]->id)
            ->assertJsonPath('data.questions.0.pivot.score', '3.00');

        $this->withToken($token)
            ->putJson("/api/admin/papers/{$paper->id}", [
                'title' => '更新试卷',
                'is_active' => false,
                'questions' => [
                    ['id' => $questions[1]->id, 'score' => 4],
                ],
            ])
            ->assertOk()
            ->assertJsonPath('data.title', '更新试卷')
            ->assertJsonPath('data.is_active', false)
            ->assertJsonPath('data.questions_count', 1);

        $this->assertDatabaseHas('paper_questions', [
            'paper_id' => $paper->id,
            'question_id' => $questions[1]->id,
            'score' => 4,
        ]);
        $this->assertDatabaseMissing('paper_questions', [
            'paper_id' => $paper->id,
            'question_id' => $questions[0]->id,
        ]);
    }

    private function bankFor(User $user, string $name): QuestionBank
    {
        return QuestionBank::create([
            'owner_id' => $user->id,
            'name' => $name,
            'visibility' => 'private',
            'is_active' => true,
        ]);
    }
}
