<?php

namespace App\Http\Controllers;

use App\Enums\PromptLandingPageVersionKind;
use App\Models\PromptLandingPage;
use App\Models\PromptLandingPageVersion;
use App\Services\PromptLandingPageEditor\DeleteDraftVersion;
use App\Services\PromptLandingPageEditor\MergeEditedDocument;
use App\Services\PromptLandingPageEditor\PublishVersion;
use App\Services\PromptLandingPageEditor\RecordPublishedVersion;
use App\Services\PromptLandingPageEditor\RestoreVersion;
use App\Services\PromptLandingPageEditor\SaveDraft;
use App\Services\PromptLandingPageEditor\SavePage;
use App\Services\PromptLandingPageEditor\SectionLibrary;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;

class PromptLandingPageEditorController extends Controller
{
    public function show(PromptLandingPage $page, SectionLibrary $library, RecordPublishedVersion $recordPublishedVersion): View
    {
        $this->authorizeEditor($page);
        $recordPublishedVersion->ensureOriginal($page, auth()->user());

        return view('prompt-landing-pages.editor', [
            'page' => $page,
            'config' => $this->clientConfig($page->refresh(), $library),
        ]);
    }

    public function data(PromptLandingPage $page, SectionLibrary $library, RecordPublishedVersion $recordPublishedVersion): JsonResponse
    {
        $this->authorizeEditor($page);
        $recordPublishedVersion->ensureOriginal($page, auth()->user());

        $page->refresh();

        $versions = $this->versionsPayload($page);

        return response()->json($this->documentPayload($page) + [
            'blocks' => $library->blocks(),
            'versions' => $versions,
            'open_version' => $this->liveOpenVersion($versions),
        ]);
    }

    public function update(Request $request, PromptLandingPage $page, SavePage $save): JsonResponse
    {
        $this->authorizeEditor($page);

        $document = $this->validatedDocument($request);
        $saved = $save->handle($page, $request->user(), $document['html'], $document['css'], $document['js']);

        return response()->json([
            'ok' => true,
            'status' => 'saved',
            'from_draft' => false,
            ...$saved,
            'open_version' => $saved['version'],
            'versions' => $this->versionsPayload($page->refresh()),
        ]);
    }

    public function draft(Request $request, PromptLandingPage $page, SaveDraft $save): JsonResponse
    {
        $this->authorizeEditor($page);

        $document = $this->validatedDocument($request);
        $draft = $save->handle($page, $request->user(), $document['html'], $document['css'], $document['js']);

        return response()->json([
            'ok' => true,
            'status' => 'draft',
            'from_draft' => false,
            ...$draft,
            'open_version' => $draft['version'],
            'versions' => $this->versionsPayload($page->refresh()),
        ]);
    }

    public function preview(Request $request, PromptLandingPage $page, MergeEditedDocument $merger): Response
    {
        $this->authorizeEditor($page);

        $document = $this->validatedDocument($request);
        $html = $merger->mergeHtml((string) ($page->html ?? ''), $document['html']);

        return response($page->assembleDocument($html, $document['css'], $document['js']), 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
            'X-Frame-Options' => 'SAMEORIGIN',
            'Content-Security-Policy' => "frame-ancestors 'self'",
        ]);
    }

    public function reset(PromptLandingPage $page): JsonResponse
    {
        $this->authorizeEditor($page);

        $page->editorDraft?->delete();
        $page->refresh();
        $versions = $this->versionsPayload($page);

        return response()->json([
            'ok' => true,
            'status' => 'saved',
            'from_draft' => false,
            ...$this->liveDocument($page),
            'open_version' => $this->liveOpenVersion($versions),
            'versions' => $versions,
        ]);
    }

    public function versions(PromptLandingPage $page, RecordPublishedVersion $recordPublishedVersion): JsonResponse
    {
        $this->authorizeEditor($page);
        $recordPublishedVersion->ensureOriginal($page, auth()->user());

        return response()->json([
            'versions' => $this->versionsPayload($page->refresh()),
        ]);
    }

    public function restore(
        PromptLandingPage $page,
        int $version,
        RestoreVersion $restore,
    ): JsonResponse {
        $this->authorizeEditor($page);

        $snapshot = $this->versionSnapshot($page, $version);
        $restored = $restore->handle($page, $snapshot);

        return response()->json([
            'ok' => true,
            'status' => 'loaded',
            'from_draft' => false,
            ...$restored,
            'open_version' => $restored['version'],
            'versions' => $this->versionsPayload($page),
        ]);
    }

    public function publishVersion(
        PromptLandingPage $page,
        int $version,
        PublishVersion $publish,
    ): JsonResponse {
        $this->authorizeEditor($page);

        $snapshot = $this->versionSnapshot($page, $version);
        $published = $publish->handle($page, $snapshot);

        return response()->json([
            'ok' => true,
            'status' => 'saved',
            'from_draft' => false,
            ...$published,
            'open_version' => $published['version'],
            'versions' => $this->versionsPayload($page->refresh()),
        ]);
    }

    public function destroyVersion(
        PromptLandingPage $page,
        int $version,
        DeleteDraftVersion $delete,
    ): JsonResponse {
        $this->authorizeEditor($page);

        $snapshot = $this->versionSnapshot($page, $version);
        $delete->handle($page, $snapshot);

        $page->refresh();
        $versions = $this->versionsPayload($page);
        $live = $this->liveDocument($page);

        return response()->json([
            'ok' => true,
            'status' => 'deleted',
            'deleted_version' => $version,
            'from_draft' => false,
            ...$live,
            'open_version' => $this->liveOpenVersion($versions),
            'versions' => $versions,
        ]);
    }

    public function previewVersion(PromptLandingPage $page, int $version): Response
    {
        $this->authorizeEditor($page);

        $snapshot = $this->versionSnapshot($page, $version);

        return response($page->assembleDocument(
            (string) ($snapshot->html ?? ''),
            (string) ($snapshot->css ?? ''),
            (string) ($snapshot->js ?? ''),
        ), 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
            'X-Frame-Options' => 'SAMEORIGIN',
            'Content-Security-Policy' => "frame-ancestors 'self'",
        ]);
    }

    private function authorizeEditor(PromptLandingPage $page): void
    {
        abort_unless($page->user_id === auth()->id(), 403);
        abort_unless($page->isReady(), 404);
        abort_if($page->isBusy(), 409, 'This page is still generating.');
    }

    /**
     * @return array{html: string, css: string, js: string}
     */
    private function validatedDocument(Request $request): array
    {
        $validated = $request->validate([
            'html' => ['present', 'nullable', 'string', 'max:1000000'],
            'css' => ['present', 'nullable', 'string', 'max:1000000'],
            'js' => ['present', 'nullable', 'string', 'max:1000000'],
        ]);

        return [
            'html' => (string) ($validated['html'] ?? ''),
            'css' => (string) ($validated['css'] ?? ''),
            'js' => (string) ($validated['js'] ?? ''),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function clientConfig(PromptLandingPage $page, SectionLibrary $library): array
    {
        $versions = $this->versionsPayload($page);

        return $this->documentPayload($page) + [
            'title' => $page->displayTitle(),
            'pageUrl' => route('prompt-landing-pages.show', $page),
            'dataUrl' => route('prompt-landing-pages.editor.data', $page),
            'saveUrl' => route('prompt-landing-pages.editor.update', $page),
            'draftUrl' => route('prompt-landing-pages.editor.draft', $page),
            'previewUrl' => route('prompt-landing-pages.editor.preview', $page),
            'resetUrl' => route('prompt-landing-pages.editor.reset', $page),
            'versionsUrl' => route('prompt-landing-pages.editor.versions', $page),
            'restoreUrlTemplate' => preg_replace(
                '#/\d+/restore$#',
                '/__VERSION__/restore',
                route('prompt-landing-pages.editor.versions.restore', [$page, 1]),
            ),
            'publishUrlTemplate' => preg_replace(
                '#/\d+/publish$#',
                '/__VERSION__/publish',
                route('prompt-landing-pages.editor.versions.publish', [$page, 1]),
            ),
            'deleteUrlTemplate' => preg_replace(
                '#/\d+$#',
                '/__VERSION__',
                route('prompt-landing-pages.editor.versions.destroy', [$page, 1]),
            ),
            'versionPreviewUrlTemplate' => preg_replace(
                '#/\d+/preview$#',
                '/__VERSION__/preview',
                route('prompt-landing-pages.editor.versions.preview', [$page, 1]),
            ),
            'savedPreviewUrl' => route('prompt-landing-pages.preview', $page),
            'token' => csrf_token(),
            'blocks' => $library->blocks(),
            'versions' => $versions,
            'open_version' => $this->liveOpenVersion($versions),
            'baseCss' => $this->canvasBaseCss(),
        ];
    }

    /**
     * @return array{html: string, css: string, js: string, from_draft: bool, saved_html: string, saved_css: string, saved_js: string}
     */
    private function documentPayload(PromptLandingPage $page): array
    {
        $live = $this->liveDocument($page);

        return $live + [
            'from_draft' => false,
            'saved_html' => $live['html'],
            'saved_css' => $live['css'],
            'saved_js' => $live['js'],
        ];
    }

    /**
     * @return array{html: string, css: string, js: string}
     */
    private function liveDocument(PromptLandingPage $page): array
    {
        return [
            'html' => (string) ($page->html ?? ''),
            'css' => (string) ($page->css ?? ''),
            'js' => (string) ($page->js ?? ''),
        ];
    }

    /**
     * @param  list<array{version: int, is_live: bool, is_original?: bool}>  $versions
     */
    private function liveOpenVersion(array $versions): ?int
    {
        foreach ($versions as $version) {
            if (! empty($version['is_live'])) {
                return (int) $version['version'];
            }
        }

        foreach ($versions as $version) {
            if (! empty($version['is_original'])) {
                return (int) $version['version'];
            }
        }

        return null;
    }

    /**
     * @return list<array{version: int, kind: string, kind_label: string, is_original: bool, is_live: bool, can_delete: bool, can_publish: bool, created_at: string|null, created_at_label: string|null, created_by: string|null}>
     */
    private function versionsPayload(PromptLandingPage $page): array
    {
        $live = $this->liveDocument($page);
        $matchesLive = static function (PromptLandingPageVersion $version) use ($live): bool {
            $kind = $version->kind ?? PromptLandingPageVersionKind::Published;

            return $kind === PromptLandingPageVersionKind::Published
                && (string) ($version->html ?? '') === $live['html']
                && (string) ($version->css ?? '') === $live['css']
                && (string) ($version->js ?? '') === $live['js'];
        };

        $versions = $page->versions()
            ->with('createdBy:id,name')
            ->orderByDesc('version')
            ->get();

        $liveVersionNumber = $versions
            ->filter($matchesLive)
            ->max('version');

        return $versions
            ->map(function (PromptLandingPageVersion $version) use ($liveVersionNumber): array {
                $kind = $version->kind ?? PromptLandingPageVersionKind::Published;
                $isOriginal = $version->version === 1 && $kind === PromptLandingPageVersionKind::Published;
                $isLive = $liveVersionNumber !== null && $version->version === $liveVersionNumber;
                $canDelete = $version->version !== 1 && ! $isLive;
                $canPublish = ! $isLive;

                $labels = [];

                if ($isOriginal) {
                    $labels[] = 'Original';
                }

                if ($isLive) {
                    $labels[] = 'Live';
                } elseif ($kind === PromptLandingPageVersionKind::Draft) {
                    $labels[] = 'Draft';
                } else {
                    $labels[] = 'Published';
                }

                return [
                    'version' => $version->version,
                    'kind' => $kind->value,
                    'kind_label' => implode(' · ', $labels),
                    'is_original' => $isOriginal,
                    'is_live' => $isLive,
                    'can_delete' => $canDelete,
                    'can_publish' => $canPublish,
                    'created_at' => $version->created_at?->toIso8601String(),
                    'created_at_label' => $version->created_at?->timezone((string) config('app.timezone'))->format('M j, Y g:i A'),
                    'created_by' => $version->createdBy?->name,
                ];
            })
            ->all();
    }

    private function versionSnapshot(PromptLandingPage $page, int $version): PromptLandingPageVersion
    {
        return $page->versions()->where('version', $version)->firstOrFail();
    }

    private function canvasBaseCss(): string
    {
        return <<<'CSS'
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; height: auto; min-height: 100%; width: 100%; max-width: 100%; overflow-x: hidden; overflow-y: visible; }
body { margin: 0; width: 100%; max-width: 100%; height: auto; min-height: 100%; overflow-x: hidden; overflow-y: visible; }
[data-gjs-type="wrapper"] { box-sizing: border-box; width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 100%; overflow-x: hidden; overflow-y: visible; }
img, video, canvas, svg, iframe { max-width: 100%; height: auto; }
.reveal, .reveal-up, .reveal-left, .reveal-right, .fade-in, .fade-up, .slide-up, .js-reveal, [data-reveal], [data-animate] { opacity: 1 !important; transform: none !important; visibility: visible !important; animation: none !important; }
details > *:not(summary) { display: block !important; height: auto !important; overflow: visible !important; }
details::details-content { display: block !important; content-visibility: visible !important; height: auto !important; }
.lp-inline-label::before, .lp-inline-label::after { content: none !important; }
@media (max-width: 991px) {
  nav, header { flex-wrap: wrap; }
}
@media (max-width: 767px) {
  body { word-wrap: break-word; overflow-wrap: anywhere; }
}
CSS;
    }
}
