<?php

declare(strict_types=1);

namespace Tests\Integration;

use Tests\Support\DatabaseSeeder;
use Tests\Support\QdbTestCase;

final class UsersAdminTest extends QdbTestCase
{
    public function testAdminCreatesCoach(): void
    {
        $token = $this->api()->loginWebAndGetToken(
            DatabaseSeeder::ADMIN_USERNAME,
            DatabaseSeeder::PASSWORD
        )['token'];
        $f = $this->fixture();
        $username = 'coach_new_' . bin2hex(random_bytes(2));

        $res = $this->api()->withToken($token, 'POST', 'users', [
            'username'     => $username,
            'password'     => 'CoachPass1!',
            'role'         => 'coach',
            'supervisorID' => $f->supervisorId,
        ]);
        $this->assertApiOk($res);
        $this->assertSame($username, $res['data']['username']);
        $this->assertSame('coach', $res['data']['role']);
        $this->assertSame($f->supervisorId, $res['data']['supervisorID']);

        $login = $this->api()->loginMobile($username, 'CoachPass1!');
        $this->assertApiOk($login);
    }

    public function testSupervisorCreatesCoachUnderSelf(): void
    {
        $token = $this->api()->loginWebAndGetToken(
            DatabaseSeeder::SUPERVISOR_USERNAME,
            DatabaseSeeder::PASSWORD
        )['token'];
        $username = 'coach_sv_' . bin2hex(random_bytes(2));

        $res = $this->api()->withToken($token, 'POST', 'users', [
            'username' => $username,
            'password' => 'CoachPass2!',
            'role'     => 'coach',
        ]);
        $this->assertApiOk($res);
        $this->assertSame($this->fixture()->supervisorId, $res['data']['supervisorID']);
    }

    public function testAdminResetsCoachPassword(): void
    {
        $token = $this->api()->loginWebAndGetToken(
            DatabaseSeeder::ADMIN_USERNAME,
            DatabaseSeeder::PASSWORD
        )['token'];
        $f = $this->fixture();

        $res = $this->api()->withToken($token, 'PUT', 'users', [
            'userID'              => $f->coachUserId,
            'newPassword'         => 'ResetPass3!',
            'mustChangePassword'  => 0,
        ]);
        $this->assertApiOk($res);

        $oldLogin = $this->api()->loginMobile(DatabaseSeeder::COACH_USERNAME, DatabaseSeeder::PASSWORD);
        $this->assertApiError($oldLogin, 'INVALID_CREDENTIALS', 401);

        $newLogin = $this->api()->loginMobile(DatabaseSeeder::COACH_USERNAME, 'ResetPass3!');
        $this->assertApiOk($newLogin);
    }

    public function testAdminDeletesCreatedCoach(): void
    {
        $token = $this->api()->loginWebAndGetToken(
            DatabaseSeeder::ADMIN_USERNAME,
            DatabaseSeeder::PASSWORD
        )['token'];
        $f = $this->fixture();
        $username = 'coach_del_' . bin2hex(random_bytes(2));

        $create = $this->api()->withToken($token, 'POST', 'users', [
            'username'     => $username,
            'password'     => 'CoachPass4!',
            'role'         => 'coach',
            'supervisorID' => $f->supervisorId,
        ]);
        $this->assertApiOk($create);
        $userId = $create['data']['userID'];

        $del = $this->api()->withToken($token, 'DELETE', 'users', ['userID' => $userId]);
        $this->assertApiOk($del);

        $list = $this->api()->withToken($token, 'GET', 'users');
        $ids = array_column($list['data']['users'], 'userID');
        $this->assertNotContains($userId, $ids);
    }

    public function testAdminCreatesSupervisor(): void
    {
        $token = $this->api()->loginWebAndGetToken(
            DatabaseSeeder::ADMIN_USERNAME,
            DatabaseSeeder::PASSWORD
        )['token'];
        $username = 'supervisor_new_' . bin2hex(random_bytes(2));
        $res = $this->api()->withToken($token, 'POST', 'users', [
            'username' => $username,
            'password' => 'SuperPass1!',
            'role'     => 'supervisor',
            'location' => 'Region B',
        ]);
        $this->assertApiOk($res);
        $this->assertSame('supervisor', $res['data']['role']);
    }

    public function testAdminReassignsCoachToSupervisor(): void
    {
        $token = $this->api()->loginWebAndGetToken(
            DatabaseSeeder::ADMIN_USERNAME,
            DatabaseSeeder::PASSWORD
        )['token'];
        $f = $this->fixture();

        $sv = $this->api()->withToken($token, 'POST', 'users', [
            'username' => 'supervisor_reassign_' . bin2hex(random_bytes(2)),
            'password' => 'SuperPass2!',
            'role'     => 'supervisor',
            'location' => 'Region C',
        ]);
        $this->assertApiOk($sv);
        $newSvId = $sv['data']['entityID'];

        $res = $this->api()->withToken($token, 'PUT', 'users', [
            'userID'       => $f->coachUserId,
            'supervisorID' => $newSvId,
        ]);
        $this->assertApiOk($res);
        $this->assertSame($newSvId, $res['data']['supervisorID']);

        [$pdo, $tmpDb, $lockFp] = qdb_open(false);
        $stmt = $pdo->prepare('SELECT supervisorID FROM coach WHERE coachID = :cid');
        $stmt->execute([':cid' => $f->coachId]);
        $this->assertSame($newSvId, $stmt->fetchColumn());
        qdb_discard($tmpDb, $lockFp);
    }

    public function testAdminReassignsClientViaAssignments(): void
    {
        $token = $this->api()->loginWebAndGetToken(
            DatabaseSeeder::ADMIN_USERNAME,
            DatabaseSeeder::PASSWORD
        )['token'];
        $f = $this->fixture();

        $newCoach = $this->api()->withToken($token, 'POST', 'users', [
            'username'     => 'coach_assign_' . bin2hex(random_bytes(2)),
            'password'     => 'CoachPass5!',
            'role'         => 'coach',
            'supervisorID' => $f->supervisorId,
        ]);
        $this->assertApiOk($newCoach);
        $newCoachId = $newCoach['data']['entityID'];

        $assign = $this->api()->withToken($token, 'POST', 'assignments', [
            'coachID'     => $newCoachId,
            'clientCodes' => [$f->clientCode],
        ]);
        $this->assertApiOk($assign);
        $this->assertSame(1, $assign['data']['assigned']);

        [$pdo, $tmpDb, $lockFp] = qdb_open(false);
        $stmt = $pdo->prepare('SELECT coachID FROM client WHERE clientCode = :cc');
        $stmt->execute([':cc' => $f->clientCode]);
        $this->assertSame($newCoachId, $stmt->fetchColumn());
        qdb_discard($tmpDb, $lockFp);
    }
}
