<?php

namespace App\Modules\Settings\Rbac\Repositories;

use App\Models\User;
use App\Support\SelectOptionFormatter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;

class TenantUserRepository
{
    public function paginate(int $tenantId, int $perPage): LengthAwarePaginator
    {
        return User::query()
            ->where('tenant_id', $tenantId)
            ->where('is_admin', false)
            ->with([
                'employee:id,tenant_id,full_name',
                'roles' => fn ($q) => $q->where('tenant_id', $tenantId),
            ])
            ->orderBy('name')
            ->paginate($perPage);
    }

    public function find(int $tenantId, int $userId): ?User
    {
        return User::query()
            ->where('tenant_id', $tenantId)
            ->where('is_admin', false)
            ->whereKey($userId)
            ->with(['employee:id,tenant_id,full_name', 'roles'])
            ->first();
    }

    public function create(array $attributes): User
    {
        return User::query()->create($attributes);
    }

    public function update(User $user, array $attributes): User
    {
        $user->forceFill($attributes)->save();

        return $user->fresh(['employee', 'roles']);
    }

    public function emailTaken(string $email, ?int $exceptUserId = null): bool
    {
        return User::query()
            ->where('email', $email)
            ->when($exceptUserId, fn ($q) => $q->whereKeyNot($exceptUserId))
            ->exists();
    }

    /**
     * @return array<int, string>
     */
    public function unlinkedOptions(int $tenantId): array
    {
        return User::query()
            ->where('tenant_id', $tenantId)
            ->where('is_admin', false)
            ->where('is_active', true)
            ->whereNull('employee_id')
            ->orderBy('name')
            ->pluck('name', 'id')
            ->all();
    }

    public function employeeLinkExists(int $tenantId, int $employeeId, int $exceptUserId): bool
    {
        return User::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->whereKeyNot($exceptUserId)
            ->exists();
    }

    public function setEmployeeId(User $user, ?int $employeeId): User
    {
        $user->forceFill(['employee_id' => $employeeId])->save();

        return $user->fresh();
    }

    public function findInvitation(string $tokenHash): ?User
    {
        return User::query()
            ->where('invitation_token', $tokenHash)
            ->where('invitation_expires_at', '>', now())
            ->where('is_active', true)
            ->first();
    }

    public function clearSessions(User $user): void
    {
        DB::table('sessions')->where('user_id', $user->id)->delete();
    }

    public function clearPasswordResetTokens(User $user): void
    {
        DB::table('password_reset_tokens')->where('email', $user->email)->delete();
    }

    public function eachForTenant(int $tenantId, callable $callback): void
    {
        User::query()
            ->where('tenant_id', $tenantId)
            ->select('id', 'tenant_id')
            ->chunkById(100, function ($users) use ($callback): void {
                foreach ($users as $user) {
                    $callback($user);
                }
            });
    }

    /** @return list<array{value: string, label: string}> */
    public function searchForLookup(int $tenantId, string $query, int $limit = 30, ?int $onlyId = null): array
    {
        $builder = User::query()
            ->where('tenant_id', $tenantId)
            ->where('is_admin', false)
            ->where('is_active', true)
            ->orderBy('name');

        if ($onlyId !== null) {
            $builder->whereKey($onlyId);
        } else {
            if ($query !== '') {
                $term = '%'.$query.'%';
                $builder->where(function ($b) use ($term): void {
                    $b->where('name', 'like', $term)->orWhere('email', 'like', $term);
                });
            }
            $builder->limit($limit);
        }

        $out = [];
        foreach ($builder->get(['id', 'name', 'email']) as $user) {
            $out[] = [
                'value' => (string) $user->id,
                'label' => SelectOptionFormatter::label($user->name, $user->email),
            ];
        }

        return $out;
    }
}
