<?php

namespace MintyPHP\Repository\Access;

use MintyPHP\DB;
use MintyPHP\Repository\Support\RepositoryArrayHelper;

/** Queries user-role assignments and counts active/inactive users per role. */
class UserRoleRepository implements UserRoleRepositoryInterface
{
    public function listRoleIdsByUserId(int $userId): array
    {
        $rows = DB::select('select role_id from user_roles where user_id = ?', (string) $userId);

        return RepositoryArrayHelper::extractIds($rows, 'user_roles', 'role_id');
    }

    public function replaceForUser(int $userId, array $roleIds): bool
    {
        DB::delete('delete from user_roles where user_id = ?', (string) $userId);
        if (!$roleIds) {
            return true;
        }

        foreach ($roleIds as $roleId) {
            DB::insert(
                'insert into user_roles (user_id, role_id, created) values (?,?,NOW())',
                (string) $userId,
                (string) $roleId
            );
        }

        return true;
    }

    public function listUserIdsByRoleIds(array $roleIds): array
    {
        $ids = RepositoryArrayHelper::sanitizePositiveIds($roleIds);
        if (!$ids) {
            return [];
        }

        $rows = DB::select(
            'select distinct user_id from user_roles where role_id in (???)',
            array_map('strval', $ids)
        );

        return RepositoryArrayHelper::sanitizePositiveIds(
            RepositoryArrayHelper::extractIds($rows, 'user_roles', 'user_id')
        );
    }

    public function countUsersByRoleIds(array $roleIds): array
    {
        return $this->countByRoleIds($roleIds, false);
    }

    public function countActiveUsersByRoleIds(array $roleIds): array
    {
        return $this->countByRoleIds($roleIds, true);
    }

    private function countByRoleIds(array $roleIds, bool $activeOnly): array
    {
        $roleIds = RepositoryArrayHelper::sanitizePositiveIds($roleIds);
        if (!$roleIds) {
            return [];
        }

        $placeholders = implode(',', array_fill(0, count($roleIds), '?'));
        $joinUsersSql = $activeOnly ? ' join users u on u.id = ur.user_id and u.active = 1' : '';
        $rows = DB::select(
            'select ur.role_id, count(distinct ur.user_id) as user_count from user_roles ur' .
            $joinUsersSql .
            ' where ur.role_id in (' . $placeholders . ') group by ur.role_id',
            ...array_map('strval', $roleIds)
        );

        if (!is_array($rows)) {
            return [];
        }

        $result = [];
        foreach ($rows as $row) {
            $roleId = $this->extractIntField($row, 'role_id');
            if ($roleId <= 0) {
                continue;
            }
            $result[$roleId] = $this->extractIntField($row, 'user_count');
        }

        return $result;
    }

    private function extractIntField(array $row, string $field): int
    {
        foreach ($row as $value) {
            if (!is_array($value)) {
                continue;
            }
            if (array_key_exists($field, $value)) {
                return (int) $value[$field];
            }
        }
        if (array_key_exists($field, $row)) {
            return (int) $row[$field];
        }
        return 0;
    }
}
