<?php

namespace App\Services;

use App\Exceptions\UserProspectUpdateDeniedException;
use App\Models\Organization;
use App\Models\User;
use App\Repositories\UserRepository;
use App\Values\ImageWritingConfig;
use App\Values\User\SsoUser;
use App\Values\User\UserCreateData;
use App\Values\User\UserUpdateData;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

class UserService
{
    public function __construct(
        private readonly UserRepository $repository,
        private readonly ImageStorage $imageStorage,
        private readonly OrganizationService $organizationService,
    ) {
    }

    public function createUser(UserCreateData $dto, ?Organization $organization = null): User
    {
        $dto->role->assertAvailable();

        $organization ??= $this->organizationService->getCurrentOrganization();
        $data = $dto->toArray();
        $data['avatar'] = $dto->avatar ? $this->maybeStoreAvatar($dto->avatar) : null;

        /** @var User $user */
        $user = $organization->users()->create($data);

        return $user->syncRoles($dto->role);
    }

    public function createOrUpdateUserFromSso(SsoUser $ssoUser): User
    {
        $existingUser = $this->repository->findOneBySso($ssoUser);

        if ($existingUser) {
            $existingUser->update([
                'avatar' => $existingUser->has_custom_avatar ? $existingUser->avatar : $ssoUser->avatar,
                'sso_id' => $ssoUser->id,
                'sso_provider' => $ssoUser->provider,
            ]);

            return $existingUser;
        }

        return $this->createUser(UserCreateData::fromSsoUser($ssoUser));
    }

    public function updateUser(User $user, UserUpdateData $dto): User
    {
        throw_if($user->is_prospect, new UserProspectUpdateDeniedException());
        $dto->role?->assertAvailable();

        $data = [
            'name' => $dto->name,
            'email' => $dto->email,
            'password' => $dto->password ?: $user->password,
            'avatar' => $dto->avatar ? $this->maybeStoreAvatar($dto->avatar) : null,
        ];

        if ($user->sso_provider) {
            // SSO users cannot change their password or email
            Arr::forget($data, ['password', 'email']);
        }

        $user->update($data);

        if ($dto->role && $user->role !== $dto->role) {
            $user->syncRoles($dto->role);
        }

        return $user->refresh(); // make sure the roles and permissions are refreshed
    }

    /**
     * @param string $avatar Either the URL of the avatar or image data
     */
    private function maybeStoreAvatar(string $avatar): string
    {
        if (Str::startsWith($avatar, ['http://', 'https://'])) {
            return $avatar;
        }

        return basename($this->imageStorage->storeImage($avatar, ImageWritingConfig::make(maxWidth: 480)));
    }

    public function deleteUser(User $user): void
    {
        $user->delete();
    }

    public function savePreference(User $user, string $key, mixed $value): void
    {
        $user->preferences = $user->preferences->set($key, $value);

        $user->save();
    }
}