<?php

namespace App\Modules\Settings\Rbac\Services;

use App\Models\User;
use App\Modules\Admin\Tenancy\Models\Tenant;
use App\Modules\Navigation\Services\NavigationService;
use App\Modules\Settings\Rbac\Repositories\TenantUserRepository;
use App\Modules\Settings\Rbac\Repositories\UserRoleRepository;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class TenantUserInviteService
{
    public function __construct(
        private readonly TenantUserDirectoryService $directory,
        private readonly TenantUserRepository $users,
        private readonly UserRoleRepository $userRoles,
        private readonly TenantUserEmployeeLinkService $employeeLinks,
        private readonly RbacAuditService $audit,
    ) {}

    /**
     * @param  list<int>  $roleIds
     * @return array{user: User, plain_token: string}
     */
    public function invite(Tenant $tenant, string $name, string $email, array $roleIds, ?int $employeeId = null): array
    {
        if ($this->directory->emailTakenInTenant($tenant, $email, null)) {
            throw ValidationException::withMessages([
                'email' => [__('rbac.validation.email_taken')],
            ]);
        }
        $token = Str::random(48);

        $result = DB::transaction(function () use ($tenant, $name, $email, $roleIds, $employeeId, $token): array {
            $user = $this->users->create([
                'name' => $name,
                'email' => $email,
                'password' => Hash::make(Str::random(32)),
                'tenant_id' => $tenant->id,
                'is_admin' => false,
                'invitation_token' => hash('sha256', $token),
                'invitation_expires_at' => now()->addDays(7),
            ]);
            $this->userRoles->syncRolesForUser($user, (int) $tenant->id, $roleIds);
            TenantPermissionEvaluator::forgetCacheForUser($user);
            NavigationService::forgetForUser($user);

            if ($employeeId !== null) {
                $this->employeeLinks->sync($tenant, $user, $employeeId);
            }

            return ['user' => $user, 'plain_token' => $token];
        });
        $this->audit->user($result['user'], 'invited');

        return $result;
    }

    public function accept(string $plainToken, string $password): User
    {
        $hash = hash('sha256', $plainToken);
        $user = $this->users->findInvitation($hash);
        if ($user === null) {
            throw ValidationException::withMessages([
                'token' => [__('rbac.validation.invite_invalid')],
            ]);
        }
        $user->forceFill([
            'password' => Hash::make($password),
            'invitation_token' => null,
            'invitation_expires_at' => null,
            'email_verified_at' => now(),
        ])->save();
        TenantPermissionEvaluator::forgetCacheForUser($user);

        return $user;
    }
}
