<?php

namespace App\Jobs;

use App\Contracts\PromptLandingPageBriefResolver;
use App\Contracts\PromptLandingPageHtmlGenerator;
use App\DTOs\PromptLandingPageBrief;
use App\Models\PromptLandingPage;
use App\Models\PromptLandingPageAttachment;
use App\Services\PromptLandingPageEditor\RecordPublishedVersion;
use App\Services\PromptLandingPageHtmlSanitizer;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;

class GeneratePromptLandingPageJob implements ShouldBeUnique, ShouldQueue
{
    use Queueable;

    public int $timeout = 2000;

    public int $tries = 1;

    public int $uniqueFor = 1800;

    public function __construct(public int $promptLandingPageId) {}

    public function uniqueId(): string
    {
        return (string) $this->promptLandingPageId;
    }

    public function handle(
        PromptLandingPageBriefResolver $briefResolver,
        PromptLandingPageHtmlGenerator $generator,
        PromptLandingPageHtmlSanitizer $sanitizer,
        RecordPublishedVersion $recordPublishedVersion,
    ): void {
        $page = PromptLandingPage::query()
            ->with(['messages', 'attachments'])
            ->findOrFail($this->promptLandingPageId);

        if ($page->isApproved()) {
            return;
        }

        $page->update([
            'status' => 'searching',
            'failure_reason' => null,
        ]);

        try {
            $brief = $this->resolveBrief($page, $briefResolver);
            $attachments = $page->attachments->all();
            $forbidden = $sanitizer->tokens($attachments);
            $revising = filled($page->html);
            $latestUserMessage = $page->messages
                ->reverse()
                ->first(fn ($message) => $message->role === 'user');

            if ($revising) {
                $recordPublishedVersion->ensureOriginal($page);
            }

            $page->update(['status' => 'generating']);

            $generated = $generator->generate(
                $brief,
                $revising ? $page->html : null,
                $revising ? ($latestUserMessage?->content) : null,
                $forbidden,
            );

            $html = $sanitizer->sanitize($generated->html, $attachments);
            $css = $sanitizer->sanitize($generated->css, $attachments);
            $js = $sanitizer->sanitize($generated->js, $attachments);

            $page->update([
                'status' => 'pending_approval',
                'title' => $generated->title !== '' ? $generated->title : $page->title,
                'html' => $html,
                'css' => $css,
                'js' => $js,
                'prompt_used' => $generated->prompt,
                'failure_reason' => null,
                'approved_by' => null,
                'approved_at' => null,
                'rejected_at' => null,
                'rejection_reason' => null,
            ]);

            if ($revising) {
                $recordPublishedVersion->snapshotLive($page);
            } else {
                $recordPublishedVersion->ensureOriginal($page);
            }

            $page->messages()->create([
                'role' => 'assistant',
                'content' => $revising
                    ? 'I have updated the landing page. Preview it below and approve it when you are happy, or send another message to revise it.'
                    : 'Your landing page is ready for review. Preview it below and approve it, or send another message to revise it.',
            ]);
        } catch (Throwable $e) {
            $page->update([
                'status' => 'failed',
                'failure_reason' => $e->getMessage(),
            ]);

            $page->messages()->create([
                'role' => 'assistant',
                'content' => "That step failed: {$e->getMessage()}\n\nNothing on the landing page was changed.",
            ]);

            throw $e;
        }
    }

    public function failed(?Throwable $exception): void
    {
        $page = PromptLandingPage::query()->find($this->promptLandingPageId);

        if ($page === null || in_array($page->status, ['pending_approval', 'approved', 'completed'], true)) {
            return;
        }

        $page->update([
            'status' => 'failed',
            'failure_reason' => $exception?->getMessage(),
        ]);
    }

    private function resolveBrief(
        PromptLandingPage $page,
        PromptLandingPageBriefResolver $briefResolver,
    ): PromptLandingPageBrief {
        $attachments = $page->attachments;
        $needsBrief = ! is_array($page->brief) || $page->brief === []
            || $attachments->contains(fn (PromptLandingPageAttachment $file) => ! $file->included_in_brief);

        if (! $needsBrief) {
            return PromptLandingPageBrief::fromArray($page->brief);
        }

        $page->update(['status' => 'analysing']);

        $prompt = $page->messages
            ->where('role', 'user')
            ->pluck('content')
            ->implode("\n\n");

        $brief = $briefResolver->resolve(
            $prompt,
            $attachments->all(),
            $page->website_url,
            $page->purpose,
        );

        $page->update(['brief' => $brief->toArray()]);
        $page->attachments()->update(['included_in_brief' => true]);
        $page->unsetRelation('attachments');
        $page->load('attachments');

        return $brief;
    }
}
