<?php declare(strict_types=1);

namespace App\Services\Private;

use App\Dto\Builder\CaptchaToken as CaptchaTokenDto;
use App\Dto\QuerySettingsDto;
use App\Dto\Request\Private\CaptchaToken\StoreUpdate;
use App\Models\User;
use App\Models\CaptchaToken;
use App\Repositories\CaptchaTokenRepository;
use App\ServiceResults\ServiceResultArray;
use App\ServiceResults\ServiceResultError;
use App\ServiceResults\ServiceResultSuccess;
use App\ServiceResults\StoreUpdateResult;
use App\Services\CaptchaToken\CaptchaTokenHandler;
use App\Services\Service;
use Illuminate\Support\Facades\DB;

final class CaptchaTokenService extends Service
{
    public function __construct(
        private readonly CaptchaTokenRepository $captchaTokenRepository,
        private readonly CaptchaTokenHandler $captchaTokenHandler,
    ) { }

    public function index(CaptchaTokenDto $captchaTokenDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
    {
        if ($user->cannot('viewAny', CaptchaToken::class)) {
            return $this->errFobidden(__('Access is denied'));
        }

        $userId = null;
        if ($user->cannot('viewAnyAll', CaptchaToken::class)) {
            $userId = $user->id;
        }

        $captchaTokens = $this->captchaTokenRepository->getCaptchaTokens(
            $captchaTokenDto,
            $querySettingsDto->getQueryWith(),
            $userId
        )->pagination(
            $querySettingsDto->getLimit(),
            $querySettingsDto->getPage()
        );

        return $this->result([
            'captchaTokens' => $captchaTokens,
        ]);
    }

    public function create(User $user): ServiceResultError | ServiceResultArray
    {
        if ($user->cannot('create', CaptchaToken::class)) {
            return $this->errFobidden(__('Access is denied'));
        }

        return $this->result([
            'captchaToken' => new CaptchaToken(),
        ]);
    }

    public function edit(int $id, User $user): ServiceResultError | ServiceResultArray
    {
        $modelCaptchaToken = $this->captchaTokenRepository->getCaptchaTokenById($id);

        if (is_null($modelCaptchaToken)) {
            return $this->errNotFound(__('Not Found'));
        }

        if ($user->cannot('view', $modelCaptchaToken)) {
            return $this->errFobidden(__('Access is denied'));
        }

        return $this->result([
            'captchaToken' => $modelCaptchaToken,
        ]);
    }

    public function store(StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
    {
        if ($user->cannot('create', CaptchaToken::class)) {
            return $this->errFobidden(__('Access is denied'));
        }

        try {
            $modelCaptchaToken = DB::transaction(function () use ($data, $user) {
                $dataCaptchaToken = $this->getDataCaptchaToken($data);
                return $this->captchaTokenHandler->handleStore($dataCaptchaToken, $user);
            });
        } catch (\Throwable $e) {
            report($e);
            return $this->errService(__('Server Error'));
        }

        return $this->resultStoreUpdateModel($modelCaptchaToken, __('Captcha token created successfully'));
    }

    public function update(int $id, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
    {
        $modelCaptchaToken = $this->captchaTokenRepository->getCaptchaTokenById($id);

        if (is_null($modelCaptchaToken)) {
            return $this->errNotFound(__('Not Found'));
        }

        if ($user->cannot('update', $modelCaptchaToken)) {
            return $this->errFobidden(__('Access is denied'));
        }

        try {
            $modelCaptchaToken = DB::transaction(function () use ($data, $modelCaptchaToken) {
                $dataCaptchaToken = $this->getDataCaptchaToken($data);

                return $this->captchaTokenHandler->handleUpdate($modelCaptchaToken, $dataCaptchaToken);
            });
        } catch (\Throwable $e) {
            report($e);
            return $this->errService(__('Server Error'));
        }

        return $this->resultStoreUpdateModel($modelCaptchaToken, __('Captcha token updated successfully'));
    }

    public function destroy(int $id, User $user): ServiceResultError | ServiceResultSuccess
    {
        $modelCaptchaToken = $this->captchaTokenRepository->getCaptchaTokenById($id);

        if (is_null($modelCaptchaToken)) {
            return $this->errNotFound(__('Not Found'));
        }

        if ($user->cannot('delete', $modelCaptchaToken)) {
            return $this->errFobidden(__('Access is denied'));
        }

        try {
            DB::transaction(function () use ($modelCaptchaToken) {
                $this->captchaTokenHandler->handleDestroy($modelCaptchaToken);
            });
        } catch (\Throwable $e) {
            report($e);
            return $this->errService(__('Server Error'));
        }

        return $this->ok(__('The captcha token has been removed'));
    }

    private function getDataCaptchaToken(StoreUpdate $data): array
    {
        return [
            'title' => $data->getTitle(),
        ];
    }
}