<?php

namespace Tests\Feature;

use App\Models\GameAccountUser;
use App\Services\AzerothCoreAccountService;
use App\Services\InviteCodeService;
use Illuminate\Support\Collection;
use Mockery\MockInterface;
use Tests\TestCase;

class AdminAccountsFeatureTest extends TestCase
{
    public function test_guest_is_redirected_to_login_for_admin(): void
    {
        $response = $this->get(route('admin.accounts.index'));

        $response->assertRedirect(route('login'));
    }

    public function test_regular_player_cannot_open_admin_accounts_page(): void
    {
        $response = $this->actingAs($this->makeUser(0))->get(route('admin.accounts.index'));

        $response->assertForbidden();
    }

    public function test_admin_can_open_accounts_page(): void
    {
        $this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
            $mock->shouldReceive('accountTypeStats')
                ->once()
                ->with(null)
                ->andReturn(['players' => 1, 'bots' => 1]);

            $mock->shouldReceive('listAccounts')
                ->once()
                ->with(null, 'players')
                ->andReturn(new Collection([
                    [
                        'id' => 1,
                        'username' => 'ADMIN',
                        'email' => 'ADMIN@EXAMPLE.COM',
                        'reg_mail' => 'ADMIN@EXAMPLE.COM',
                        'gm_level' => 3,
                        'access_label' => 'Администратор',
                        'locked' => false,
                        'online' => false,
                        'joined_at' => '12.03.2026 12:00',
                        'last_login_at' => '13.03.2026 18:00',
                        'characters' => new Collection([
                            [
                                'name' => 'Stormmage',
                                'race' => 'Человек',
                                'class' => 'Маг',
                                'level' => 80,
                                'online' => false,
                            ],
                        ]),
                    ],
                ]));
        });

        $this->mock(InviteCodeService::class, function (MockInterface $mock): void {
            $mock->shouldReceive('latest')
                ->once()
                ->andReturn(new Collection());

            $mock->shouldReceive('stats')
                ->once()
                ->andReturn(['total' => 0, 'available' => 0, 'redeemed' => 0]);
        });

        $response = $this->actingAs($this->makeUser(3))->get(route('admin.accounts.index'));

        $response->assertOk();
        $response->assertSee('Stormmage');
        $response->assertSee('Аккаунты и персонажи');
        $response->assertSee('Управление инвайт-кодами');
        $response->assertSee('Игроки · 1');
        $response->assertSee('Боты · 1');
    }

    public function test_admin_can_open_bots_tab(): void
    {
        $this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
            $mock->shouldReceive('accountTypeStats')
                ->once()
                ->with(null)
                ->andReturn(['players' => 4, 'bots' => 2]);

            $mock->shouldReceive('listAccounts')
                ->once()
                ->with(null, 'bots')
                ->andReturn(new Collection([
                    [
                        'id' => 7,
                        'username' => 'RNDBOT0007',
                        'email' => '',
                        'reg_mail' => '',
                        'gm_level' => 0,
                        'access_label' => 'Игрок',
                        'locked' => false,
                        'online' => false,
                        'joined_at' => '14.03.2026 01:00',
                        'last_login_at' => '14.03.2026 01:30',
                        'characters' => new Collection(),
                    ],
                ]));
        });

        $this->mock(InviteCodeService::class, function (MockInterface $mock): void {
            $mock->shouldReceive('latest')
                ->once()
                ->andReturn(new Collection());

            $mock->shouldReceive('stats')
                ->once()
                ->andReturn(['total' => 0, 'available' => 0, 'redeemed' => 0]);
        });

        $response = $this->actingAs($this->makeUser(3))->get(route('admin.accounts.index', ['tab' => 'bots']));

        $response->assertOk();
        $response->assertSee('RNDBOT0007');
        $response->assertSee('Боты · 2');
    }

    public function test_admin_can_update_access_level(): void
    {
        $this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
            $mock->shouldReceive('findUserById')
                ->once()
                ->with(12)
                ->andReturn($this->makeUser(0));

            $mock->shouldReceive('updateAccessLevel')
                ->once()
                ->with(12, 2);
        });

        $response = $this->from(route('admin.accounts.index'))
            ->actingAs($this->makeUser(3))
            ->patch(route('admin.accounts.access.update', 12), [
                'gmlevel' => 2,
            ]);

        $response->assertRedirect(route('admin.accounts.index'));
        $response->assertSessionHas('status', 'Уровень доступа обновлён.');
    }

    public function test_admin_can_create_invite_codes(): void
    {
        $admin = $this->makeUser(3);

        $this->mock(InviteCodeService::class, function (MockInterface $mock) use ($admin): void {
            $mock->shouldReceive('createCodes')
                ->once()
                ->with(3, $admin)
                ->andReturn(new Collection([1, 2, 3]));
        });

        $response = $this->from(route('admin.accounts.index'))
            ->actingAs($admin)
            ->post(route('admin.invite-codes.store'), [
                'quantity' => 3,
            ]);

        $response->assertRedirect(route('admin.accounts.index'));
        $response->assertSessionHas('status', 'Создано инвайт-кодов: 3.');
    }

    private function makeUser(int $gmLevel): GameAccountUser
    {
        return new GameAccountUser(
            id: 1,
            username: 'ADMIN',
            email: 'ADMIN@EXAMPLE.COM',
            regMail: 'ADMIN@EXAMPLE.COM',
            gmLevel: $gmLevel,
            locked: false,
            lastLoginAt: '2026-03-13 18:00:00',
            joinedAt: '2026-03-12 12:00:00',
            saltHex: str_repeat('AA', 32),
            verifierHex: str_repeat('BB', 32),
        );
    }
}
