<?php

declare(strict_types=1);

namespace Builder\Core\Http\Controllers\Resource;

use Builder\Core\Exceptions\ResourceNotFoundException;
use Builder\Core\Exceptions\UnauthorizedAccessException;
use Builder\Core\Foundation\Contracts\BuilderAccessible;
use Builder\Core\Foundation\Enums\ToastType;
use Builder\Core\Foundation\Middleware\BuilderAuth;
use Builder\Core\Foundation\Registry;
use Builder\Core\Foundation\Resources\ResourceDefinition;
use Builder\Core\Foundation\Revisions\RevisionService;
use Builder\Core\Foundation\Support\Toast;
use Builder\Core\Models\Revision;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

final class ResourceRevisionRestoreController
{
    public function __construct(
        private readonly Registry $registry,
        private readonly RevisionService $revisionService,
    ) {}

    public function __invoke(Request $request, string $resource, int|string $id, int $revisionId): RedirectResponse
    {
        $definition = $this->resolveRevisableOrFail($resource);
        $this->authorizeResource($request, $definition, 'update');

        $model = $definition->getModel();
        $record = $model::findOrFail($id);

        $revision = Revision::query()
            ->where('revisable_type', $model)
            ->where('revisable_id', $id)
            ->findOrFail($revisionId);

        $this->revisionService->restore($revision, $record);

        $guard = BuilderAuth::guard();

        $this->revisionService->capture(
            $record->fresh() ?? $record,
            $definition,
            $request->user($guard)?->getKey(),
            isRestore: true,
            restoredFrom: $revisionId,
        );

        return redirect()->route('builder.resource.edit', ['resource' => $resource, 'id' => $id])
            ->with(Toast::flash(ToastType::Success, __('builder::builder.revisions.restored')));
    }

    private function resolveRevisableOrFail(string $resource): ResourceDefinition
    {
        $definition = $this->registry->resolveBySlug($resource);

        if ($definition === null) {
            throw ResourceNotFoundException::forSlug($resource);
        }

        abort_unless($definition->isRevisable(), 404);

        return $definition;
    }

    private function authorizeResource(Request $request, ResourceDefinition $definition, string $action): void
    {
        $permission = $definition->resolvePermissionForAction($action);

        if ($permission === null) {
            return;
        }

        /** @var BuilderAccessible $user */
        $user = $request->user(BuilderAuth::guard());

        if (! (method_exists($user, 'hasPermissionTo') && $user->hasPermissionTo($permission))) {
            throw UnauthorizedAccessException::forPermission($permission);
        }
    }
}
