<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\UserRepository;
use InvalidArgumentException;
use PDO;
use RuntimeException;

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

    public function create(array $input): int
    {
        $username = trim((string)($input['username'] ?? ''));
        $fullName = trim((string)($input['full_name'] ?? ''));
        $password = (string)($input['password'] ?? '');
        $isActive = !empty($input['is_active']) ? 1 : 0;
        $cashboxId = (int)($input['cashbox_id'] ?? 0);
        $roleIds = $this->normalizeRoleIds($input['role_ids'] ?? []);

        if ($username === '' || $password === '') {
            throw new InvalidArgumentException('اسم المستخدم وكلمة المرور مطلوبة');
        }

        if ($this->users->usernameExists($username)) {
            throw new InvalidArgumentException('اسم المستخدم مستخدم بالفعل');
        }

        try {
            $this->pdo->beginTransaction();
            $userId = $this->users->create([
                'username' => $username,
                'password_hash' => password_hash($password, PASSWORD_BCRYPT),
                'full_name' => $fullName,
                'is_active' => $isActive,
                'cashbox_id' => $cashboxId,
            ]);
            $this->users->replaceRoles($userId, $roleIds);
            $this->pdo->commit();
            return $userId;
        } catch (\Throwable $e) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $e;
        }
    }

    public function update(int $id, array $input, int $currentUserId): void
    {
        if ($id <= 0) {
            throw new InvalidArgumentException('مستخدم غير صحيح');
        }

        $existing = $this->users->findById($id);
        if (!$existing) {
            throw new RuntimeException('المستخدم غير موجود');
        }

        $username = trim((string)($input['username'] ?? ''));
        $fullName = trim((string)($input['full_name'] ?? ''));
        $password = (string)($input['password'] ?? '');
        $isActive = !empty($input['is_active']) ? 1 : 0;
        $cashboxId = (int)($input['cashbox_id'] ?? 0);
        $roleIds = $this->normalizeRoleIds($input['role_ids'] ?? []);

        if ($username === '') {
            throw new InvalidArgumentException('اسم المستخدم مطلوب');
        }

        if ($this->users->usernameExists($username, $id)) {
            throw new InvalidArgumentException('اسم المستخدم مستخدم بالفعل');
        }

        if ($id === $currentUserId) {
            $isActive = 1;
        }

        try {
            $this->pdo->beginTransaction();
            $this->users->update($id, [
                'username' => $username,
                'full_name' => $fullName,
                'is_active' => $isActive,
                'cashbox_id' => $cashboxId,
                'password_hash' => $password !== '' ? password_hash($password, PASSWORD_BCRYPT) : null,
            ]);
            $this->users->replaceRoles($id, $roleIds);
            $this->pdo->commit();
            rbac_flush_cache();
        } catch (\Throwable $e) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $e;
        }
    }

    public function delete(int $id, int $currentUserId): void
    {
        if ($id <= 0) {
            throw new InvalidArgumentException('مستخدم غير صحيح');
        }
        if ($id === $currentUserId) {
            throw new InvalidArgumentException('لا يمكن حذف المستخدم الحالي');
        }
        if (!$this->users->findById($id)) {
            throw new RuntimeException('المستخدم غير موجود');
        }

        $this->users->delete($id);
    }

    private function normalizeRoleIds(mixed $roleIds): array
    {
        if (!is_array($roleIds)) {
            return [];
        }

        $clean = [];
        foreach ($roleIds as $roleId) {
            $value = (int)$roleId;
            if ($value > 0) {
                $clean[$value] = $value;
            }
        }

        return array_values($clean);
    }
}
