<?php

declare(strict_types=1);

namespace App\Modules\Administration\Services;

use App\Modules\Administration\Repositories\UserRepository;
use RuntimeException;

final class UserService
{
    public function __construct(
        private UserRepository $users
    ) {
    }

    public function all(): array
    {
        return $this->users->all();
    }

    public function find(int $id): ?array
    {
        return $this->users->find($id);
    }

    public function roles(): array
    {
        return $this->users->roles();
    }

    public function userRoleId(int $userId): int
    {
        return $this->users->userRoleId($userId);
    }

    public function create(array $data): void
    {
        $validated = $this->validateUserData(
            $data,
            null,
            true
        );

        $this->users->createWithRole(
            [
                'full_name' => $validated['full_name'],
                'username' => $validated['username'],
                'email' => $validated['email'],
                'password' => password_hash(
                    $validated['password'],
                    PASSWORD_DEFAULT
                ),
                'active' => 1
            ],
            $validated['role_id']
        );
    }

    public function update(
        int $userId,
        array $data
    ): void {
        $user = $this->users->find($userId);

        if ($user === null) {
            throw new RuntimeException(
                'User not found.'
            );
        }

        $validated = $this->validateUserData(
            $data,
            $userId,
            false
        );

        $active = isset($data['active'])
            ? 1
            : 0;

        $this->users->updateWithRole(
            $userId,
            [
                'full_name' => $validated['full_name'],
                'username' => $validated['username'],
                'email' => $validated['email'],
                'active' => $active
            ],
            $validated['role_id']
        );
    }

    public function toggleStatus(
        int $userId,
        int $currentUserId
    ): void {
        if ($userId === $currentUserId) {
            throw new RuntimeException(
                'You cannot deactivate your own account.'
            );
        }

        $user = $this->users->find($userId);

        if ($user === null) {
            throw new RuntimeException(
                'User not found.'
            );
        }

        $newStatus =
            (int) $user['active'] === 1
                ? 0
                : 1;

        $this->users->setActive(
            $userId,
            $newStatus
        );
    }

    public function resetPassword(
        int $userId,
        string $password,
        string $confirmation
    ): void {
        if ($this->users->find($userId) === null) {
            throw new RuntimeException(
                'User not found.'
            );
        }

        if (strlen($password) < 8) {
            throw new RuntimeException(
                'Password must contain at least 8 characters.'
            );
        }

        if ($password !== $confirmation) {
            throw new RuntimeException(
                'Password confirmation does not match.'
            );
        }

        $this->users->resetPassword(
            $userId,
            password_hash(
                $password,
                PASSWORD_DEFAULT
            )
        );
    }

    private function validateUserData(
        array $data,
        ?int $ignoreUserId,
        bool $requirePassword
    ): array {
        $fullName = trim(
            (string) ($data['full_name'] ?? '')
        );

        $username = trim(
            (string) ($data['username'] ?? '')
        );

        $email = trim(
            (string) ($data['email'] ?? '')
        );

        $roleId = (int) (
            $data['role_id'] ?? 0
        );

        if (
            $fullName === ''
            || $username === ''
            || $email === ''
            || $roleId < 1
        ) {
            throw new RuntimeException(
                'All required fields must be completed.'
            );
        }

        if (!filter_var(
            $email,
            FILTER_VALIDATE_EMAIL
        )) {
            throw new RuntimeException(
                'Invalid email address.'
            );
        }

        $usernameUser =
            $this->users->findByUsername($username);

        if (
            $usernameUser !== null
            && (int) $usernameUser['id'] !== $ignoreUserId
        ) {
            throw new RuntimeException(
                'Username already exists.'
            );
        }

        $emailUser =
            $this->users->findByEmail($email);

        if (
            $emailUser !== null
            && (int) $emailUser['id'] !== $ignoreUserId
        ) {
            throw new RuntimeException(
                'Email already exists.'
            );
        }

        if (!$this->users->roleExists($roleId)) {
            throw new RuntimeException(
                'Selected role is invalid.'
            );
        }

        $result = [
            'full_name' => $fullName,
            'username' => $username,
            'email' => $email,
            'role_id' => $roleId
        ];

        if ($requirePassword) {

            $password = (string) (
                $data['password'] ?? ''
            );

            $confirmation = (string) (
                $data['password_confirmation'] ?? ''
            );

            if (strlen($password) < 8) {
                throw new RuntimeException(
                    'Password must contain at least 8 characters.'
                );
            }

            if ($password !== $confirmation) {
                throw new RuntimeException(
                    'Password confirmation does not match.'
                );
            }

            $result['password'] = $password;
        }

        return $result;
    }
}