<?php

namespace App\Http\Controllers;

use App\Jobs\GeneratePromptLandingPageJob;
use App\Models\PromptLandingPage;
use App\Models\PromptLandingPageAttachment;
use App\Models\PromptLandingPageLead;
use App\Models\PromptLandingPageMessage;
use App\Models\User;
use App\Services\PromptLandingPageEditor\DuplicatePage;
use App\Services\PromptLandingPageEditor\OverrideWithPage;
use App\Support\Markdown;
use App\Support\PromptLandingPagePixels;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use Laravel\Ai\Models\Conversation;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Throwable;

class PromptLandingPageController extends Controller
{
    public function index(Request $request): RedirectResponse
    {
        return redirect()->route('landing-pages.index', array_merge(
            ['tab' => 'prompt'],
            $request->query(),
        ));
    }

    /**
     * @return array{
     *     pages: LengthAwarePaginator,
     *     purposeTabs: list<string>,
     *     activePurpose: ?string,
     *     websites: list<array{value: string, label: string}>,
     *     activeWebsite: ?string,
     *     stats: array{all: int, approved: int, active: int, failed: int},
     *     sort: string,
     *     dir: string,
     *     overrideSources: list<array{id: int, title: string, purpose: string, website: string, label: string}>
     * }
     */
    public function indexData(Request $request): array
    {
        $user = $request->user();
        $purposeTabs = $this->purposeTabs($user);

        $sortable = ['id', 'title', 'status', 'created'];
        $sort = in_array($request->query('sort'), $sortable, true) ? $request->query('sort') : 'created';
        $dir = $request->query('dir') === 'asc' ? 'asc' : 'desc';
        $perPage = in_array((int) $request->query('per_page'), [10, 25, 50], true) ? (int) $request->query('per_page') : 10;

        $requestedPurpose = (string) $request->query('purpose', '');
        $activePurpose = in_array($requestedPurpose, $purposeTabs, true)
            ? $requestedPurpose
            : ($purposeTabs[0] ?? null);

        $websites = $activePurpose !== null ? $this->websitesFor($user, $activePurpose) : [];
        $websiteValues = array_column($websites, 'value');
        $requestedWebsite = (string) $request->query('website', '');
        $activeWebsite = in_array($requestedWebsite, $websiteValues, true)
            ? $requestedWebsite
            : null;

        $query = $user->promptLandingPages()
            ->withCount([
                'leads as leads_count' => fn ($leads) => $leads->submitted(),
                'clickouts as download_clicks_count',
            ])
            ->when($activePurpose !== null, fn ($pages) => $pages->forPurposeTab($activePurpose))
            ->when($activeWebsite !== null, fn ($pages) => $pages->where('website_url', $activeWebsite))
            ->when($request->filled('status') && $request->query('status') !== 'all', function ($pages) use ($request): void {
                $status = (string) $request->query('status');

                match ($status) {
                    'pending_approval' => $pages->whereIn('status', ['pending_approval', 'completed']),
                    'in_progress' => $pages->whereIn('status', ['queued', 'searching', 'analysing', 'generating', 'draft']),
                    'failed' => $pages->whereIn('status', ['failed', 'rejected']),
                    default => $pages->where('status', $status),
                };
            })
            ->when($request->filled('q'), function ($pages) use ($request): void {
                $term = '%'.$request->query('q').'%';

                $pages->where(function ($inner) use ($term): void {
                    $inner->where('title', 'like', $term)
                        ->orWhere('website_url', 'like', $term);
                });
            })
            ->orderBy($sort === 'created' ? 'created_at' : $sort, $dir);

        $pages = $query->paginate($perPage)->withQueryString();

        $stats = [
            'all' => $user->promptLandingPages()->count(),
            'approved' => $user->promptLandingPages()->where('status', 'approved')->count(),
            'active' => $user->promptLandingPages()->whereIn('status', ['queued', 'searching', 'analysing', 'generating', 'pending_approval', 'completed', 'draft'])->count(),
            'failed' => $user->promptLandingPages()->whereIn('status', ['failed', 'rejected'])->count(),
        ];

        return [
            'pages' => $pages,
            'purposeTabs' => $purposeTabs,
            'activePurpose' => $activePurpose,
            'websites' => $websites,
            'activeWebsite' => $activeWebsite,
            'stats' => $stats,
            'sort' => $sort,
            'dir' => $dir,
            'overrideSources' => $this->overrideSourcesFor($user),
        ];
    }

    public function create(Request $request): View
    {
        return view('prompt-landing-pages.chat', [
            'page' => null,
            'conversationId' => null,
            'transcript' => [],
            'purposes' => PromptLandingPage::PURPOSES,
            'conversations' => $this->recentCampaignConversations($request),
        ]);
    }

    public function show(Request $request, PromptLandingPage $page): View
    {
        $this->authorizePage($page);

        $page->promoteLegacyCompleted();
        $page->load(['messages.attachments']);

        return view('prompt-landing-pages.chat', [
            'page' => $page,
            'conversationId' => $page->id,
            'transcript' => $this->transcript($page),
            'purposes' => PromptLandingPage::PURPOSES,
            'conversations' => $this->recentCampaignConversations($request),
        ]);
    }

    public function setup(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'page' => ['nullable', 'integer'],
            'website_url' => ['required', 'string', 'max:255'],
            'purpose' => ['required', Rule::in(PromptLandingPage::PURPOSES)],
        ], [
            'website_url.required' => 'Enter a website domain such as lucos.com or https://lucos.com.',
            'purpose.required' => 'Select the purpose of this landing page.',
            'purpose.in' => 'Select the purpose of this landing page.',
        ]);

        $websiteUrl = PromptLandingPage::normalizeWebsiteUrl($validated['website_url']);

        if ($websiteUrl === null) {
            throw ValidationException::withMessages([
                'website_url' => 'Enter a website domain such as lucos.com or https://lucos.com.',
            ]);
        }

        $purpose = trim($validated['purpose']);

        $page = isset($validated['page'])
            ? $this->ownedPage($request, (int) $validated['page'])
            : null;

        if ($page === null) {
            $page = $request->user()->promptLandingPages()->create([
                'status' => 'draft',
                'website_url' => $websiteUrl,
                'purpose' => $purpose,
            ]);
        } else {
            $this->authorizePage($page);

            $briefReset = $page->website_url !== $websiteUrl || $page->purpose !== $purpose;

            $page->update([
                'website_url' => $websiteUrl,
                'purpose' => $purpose,
                ...($briefReset ? ['brief' => null] : []),
            ]);
        }

        $page->refresh();

        return response()->json($this->setupPayload($page));
    }

    public function send(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'message' => ['nullable', 'string'],
            'page' => ['nullable', 'integer'],
            'files' => ['nullable', 'array', 'max:5'],
            'files.*' => ['file', 'mimes:pdf,txt,md,jpg,jpeg,png,gif,webp', 'max:10240'],
        ]);

        if (blank($validated['message'] ?? null) && ! $request->hasFile('files')) {
            throw ValidationException::withMessages([
                'message' => 'Enter a prompt or attach a PDF, text document, or image.',
            ]);
        }

        $page = isset($validated['page'])
            ? $this->ownedPage($request, (int) $validated['page'])
            : null;

        if ($page === null || ! $page->hasSetup()) {
            throw ValidationException::withMessages([
                'website_url' => 'Save a website domain and the landing page purpose before generating.',
            ]);
        }

        $this->authorizePage($page);

        if ($page->isApproved()) {
            throw ValidationException::withMessages([
                'message' => 'This landing page is approved. Start a new landing page to generate another one.',
            ]);
        }

        if ($page->isBusy() && $page->messages()->exists()) {
            return response()->json([
                'message' => 'A landing page is already generating. Wait for it to finish before sending another message.',
            ], 422);
        }

        $message = $this->promptFor($validated, $request);

        $userMessage = $page->messages()->create([
            'role' => 'user',
            'content' => $message,
        ]);

        if (blank($page->title)) {
            $page->title = Str::limit($message, 80, '');
        }

        foreach ($request->file('files', []) as $file) {
            $this->storeAttachment($page, $file, $userMessage);
        }

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

        try {
            GeneratePromptLandingPageJob::dispatch($page->id);
        } catch (Throwable $e) {
            // The job records failure before rethrowing; the sync driver would
            // otherwise surface that as a 500.
        }

        $page->refresh()->load(['messages.attachments']);

        return response()->json($this->payload($page));
    }

    public function status(PromptLandingPage $page): JsonResponse
    {
        $this->authorizePage($page);

        $page->load('messages');

        return response()->json($this->payload($page));
    }

    public function leads(Request $request, PromptLandingPage $page): JsonResponse|View
    {
        $this->authorizePage($page);

        $tab = match ($request->query('tab')) {
            'views' => 'views',
            'downloads' => $page->isAppDownload() ? 'downloads' : 'leads',
            default => 'leads',
        };

        if ($request->expectsJson()) {
            if ($tab === 'downloads') {
                return response()->json([
                    'public_url' => $page->publicUrl(),
                    'tab' => $tab,
                    'clickouts' => $page->clickouts()
                        ->with('visit:id,query_params')
                        ->latest('id')
                        ->limit(100)
                        ->get(['id', 'destination_url', 'prompt_landing_page_lead_id', 'created_at'])
                        ->map(fn ($clickout): array => [
                            'id' => $clickout->id,
                            'destination_url' => $clickout->destination_url,
                            'query_params' => $clickout->visit?->query_params,
                            'created_at' => $clickout->created_at?->toIso8601String(),
                        ])
                        ->values()
                        ->all(),
                ]);
            }

            return response()->json([
                'public_url' => $page->publicUrl(),
                'tab' => $tab,
                'leads' => $this->leadsForTab($page, $tab)
                    ->limit(100)
                    ->get(['id', 'payload', 'query_params', 'created_at'])
                    ->map(fn ($lead): array => [
                        'id' => $lead->id,
                        'payload' => $lead->payload,
                        'query_params' => $lead->query_params,
                        'created_at' => $lead->created_at?->toIso8601String(),
                    ])
                    ->values()
                    ->all(),
            ]);
        }

        $sortable = ['id', 'created'];
        $sort = in_array($request->query('sort'), $sortable, true) ? $request->query('sort') : 'created';
        $dir = $request->query('dir') === 'asc' ? 'asc' : 'desc';
        $perPage = in_array((int) $request->query('per_page'), [10, 25, 50], true) ? (int) $request->query('per_page') : 10;

        if ($tab === 'downloads') {
            $clickouts = $page->clickouts()
                ->with('visit:id,query_params')
                ->when($request->filled('q'), function ($query) use ($request): void {
                    $term = '%'.$request->query('q').'%';
                    $query->where(function ($inner) use ($term): void {
                        $inner->where('destination_url', 'like', $term)
                            ->orWhereHas('visit', fn ($visit) => $visit->where('query_params', 'like', $term));
                    });
                })
                ->orderBy($sort === 'created' ? 'created_at' : $sort, $dir)
                ->paginate($perPage)
                ->withQueryString();

            return view('prompt-landing-pages.leads', [
                'page' => $page,
                'leads' => $clickouts,
                'fields' => [],
                'stats' => [
                    'all' => $page->clickouts()->count(),
                    'today' => $page->clickouts()->where('created_at', '>=', now()->startOfDay())->count(),
                    'week' => $page->clickouts()->where('created_at', '>=', now()->subDays(7))->count(),
                    'month' => $page->clickouts()->where('created_at', '>=', now()->startOfMonth())->count(),
                ],
                'tab' => $tab,
                'tabCounts' => [
                    'leads' => $page->leads()->submitted()->count(),
                    'views' => $page->leads()->pageViews()->count(),
                    'downloads' => $page->clickouts()->count(),
                ],
                'sort' => $sort,
                'dir' => $dir,
            ]);
        }

        $leads = $this->leadsForTab($page, $tab)
            ->when($request->filled('q'), function ($query) use ($request): void {
                $term = '%'.$request->query('q').'%';
                $query->where(function ($inner) use ($term): void {
                    $inner->where('payload', 'like', $term)
                        ->orWhere('query_params', 'like', $term);
                });
            })
            ->orderBy($sort === 'created' ? 'created_at' : $sort, $dir)
            ->paginate($perPage)
            ->withQueryString();

        return view('prompt-landing-pages.leads', [
            'page' => $page,
            'leads' => $leads,
            'fields' => $tab === 'leads' ? $this->leadPayloadFields($leads) : [],
            'stats' => [
                'all' => $this->leadsForTab($page, $tab)->count(),
                'today' => $this->leadsForTab($page, $tab)->where('created_at', '>=', now()->startOfDay())->count(),
                'week' => $this->leadsForTab($page, $tab)->where('created_at', '>=', now()->subDays(7))->count(),
                'month' => $this->leadsForTab($page, $tab)->where('created_at', '>=', now()->startOfMonth())->count(),
            ],
            'tab' => $tab,
            'tabCounts' => [
                'leads' => $page->leads()->submitted()->count(),
                'views' => $page->leads()->pageViews()->count(),
                'downloads' => $page->isAppDownload() ? $page->clickouts()->count() : 0,
            ],
            'sort' => $sort,
            'dir' => $dir,
        ]);
    }

    public function preview(PromptLandingPage $page): Response
    {
        $this->authorizePage($page);
        abort_unless($page->isReady(), 404);

        return response($page->renderedDocument(), 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
        ]);
    }

    public function approve(Request $request, PromptLandingPage $page): JsonResponse
    {
        $this->authorizePage($page);
        $this->assertPendingApproval($page);

        $page->approve($request->user());
        $page->refresh()->load(['messages.attachments']);

        return response()->json($this->payload($page));
    }

    public function reject(Request $request, PromptLandingPage $page): JsonResponse
    {
        $this->authorizePage($page);
        $this->assertPendingApproval($page);

        $validated = $request->validate([
            'reason' => ['nullable', 'string', 'max:2000'],
        ]);

        $reason = filled($validated['reason'] ?? null)
            ? trim((string) $validated['reason'])
            : null;

        $page->reject($reason === '' ? null : $reason);
        $page->refresh()->load(['messages.attachments']);

        return response()->json($this->payload($page));
    }

    public function download(PromptLandingPage $page): Response
    {
        $this->authorizePage($page);
        abort_unless($page->canDownload(), 404);

        $filename = Str::slug($page->title ?: 'landing-page').'.html';

        return response($page->renderedDocument(), 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="'.$filename.'"',
        ]);
    }

    public function attachment(
        Request $request,
        PromptLandingPage $page,
        PromptLandingPageAttachment $attachment,
    ): StreamedResponse {
        $this->authorizePage($page);
        abort_unless($attachment->prompt_landing_page_id === $page->id, 404);
        abort_unless(Storage::disk('local')->exists($attachment->path), 404);

        return Storage::disk('local')->response(
            $attachment->path,
            $attachment->original_name,
            [
                'Content-Type' => $this->inlineMime($attachment),
                'X-Content-Type-Options' => 'nosniff',
                'Cache-Control' => 'private, no-store',
            ],
            $request->boolean('download') ? 'attachment' : 'inline',
        );
    }

    public function duplicate(Request $request, PromptLandingPage $page, DuplicatePage $duplicatePage): RedirectResponse
    {
        $this->authorizePage($page);

        $user = $request->user();
        abort_unless($user instanceof User, 403);

        $copy = $duplicatePage->handle($page, $user);

        return redirect()
            ->route('prompt-landing-pages.show', $copy)
            ->with('status', 'Duplicated “'.$page->displayTitle().'” as a new landing page.');
    }

    public function override(Request $request, PromptLandingPage $page, OverrideWithPage $overrideWithPage): RedirectResponse
    {
        $this->authorizePage($page);

        $validated = $request->validate([
            'source_page' => ['required', 'integer'],
        ]);

        $source = $this->ownedPage($request, (int) $validated['source_page']);
        $user = $request->user();
        abort_unless($user instanceof User, 403);
        $overrideWithPage->handle($page, $source, $user);

        return redirect()
            ->route('prompt-landing-pages.show', $page)
            ->with('status', 'Replaced this page with content from “'.$source->displayTitle().'”. The previous version was saved.');
    }

    public function destroy(PromptLandingPage $page): RedirectResponse
    {
        $this->authorizePage($page);

        $page->delete();

        return redirect()->route('prompt-landing-pages.index');
    }

    public function updatePixels(Request $request, PromptLandingPage $page): RedirectResponse
    {
        $this->authorizePage($page);
        abort_unless($page->isApproved(), 404);

        $validator = Validator::make($request->all(), [
            'google_conversion_send_to' => ['nullable', 'string', 'max:80', function (string $attribute, mixed $value, \Closure $fail): void {
                if (! is_string($value) || ! PromptLandingPagePixels::isGoogleSendTo($value)) {
                    $fail('Enter a Google Ads send_to id like AW-1234567890/AbCdEfGh.');
                }
            }],
            'meta_pixel_id' => ['nullable', 'string', 'max:20', function (string $attribute, mixed $value, \Closure $fail): void {
                if (! is_string($value) || ! PromptLandingPagePixels::isMetaPixelId($value)) {
                    $fail('Enter a Meta Pixel id made of digits only.');
                }
            }],
        ]);

        if ($validator->fails()) {
            return back()
                ->withErrors($validator)
                ->withInput()
                ->with('pixels_action', route('prompt-landing-pages.pixels.update', $page));
        }

        $validated = $validator->validated();

        $page->update([
            'google_conversion_send_to' => $validated['google_conversion_send_to'] ?? null,
            'meta_pixel_id' => $validated['meta_pixel_id'] ?? null,
        ]);

        return back()->with('status', 'Tracking pixels saved.');
    }

    private function authorizePage(PromptLandingPage $page): void
    {
        abort_unless($page->user_id === auth()->id(), 403);
    }

    private function leadsForTab(PromptLandingPage $page, string $tab)
    {
        return $tab === 'views'
            ? $page->leads()->pageViews()
            : $page->leads()->submitted();
    }

    /**
     * @param  iterable<int, PromptLandingPageLead>  $leads
     * @return list<string>
     */
    private function leadPayloadFields(iterable $leads): array
    {
        $fields = [];

        foreach ($leads as $lead) {
            foreach (array_keys($lead->payload ?? []) as $field) {
                $fields[$field] = true;
            }
        }

        return array_keys($fields);
    }

    /**
     * @return list<string>
     */
    private function purposeTabs(User $user): array
    {
        $present = $user->promptLandingPages()
            ->whereNotNull('purpose')
            ->where('purpose', '!=', '')
            ->distinct()
            ->pluck('purpose')
            ->all();

        $tabs = array_values(array_filter(
            PromptLandingPage::PURPOSES,
            fn (string $purpose): bool => in_array($purpose, $present, true),
        ));

        $hasUnset = $user->promptLandingPages()
            ->where(function ($query): void {
                $query->whereNull('purpose')->orWhere('purpose', '');
            })
            ->exists();

        if ($hasUnset) {
            $tabs[] = PromptLandingPage::UNSET_PURPOSE;
        }

        return $tabs;
    }

    /**
     * @return list<array{id: int, title: string, purpose: string, website: string, label: string}>
     */
    private function overrideSourcesFor(User $user): array
    {
        return $user->promptLandingPages()
            ->whereNotNull('html')
            ->where('html', '!=', '')
            ->whereNotIn('status', ['queued', 'searching', 'analysing', 'generating'])
            ->orderByDesc('id')
            ->get(['id', 'title', 'purpose', 'website_url', 'status'])
            ->map(function (PromptLandingPage $page): array {
                $purpose = $page->purpose ?: 'Unset';
                $title = $page->displayTitle();
                $website = $page->displayWebsite();

                return [
                    'id' => $page->id,
                    'title' => $title,
                    'purpose' => $purpose,
                    'website' => $website,
                    'label' => '#'.$page->id.' · '.$title.' · '.$purpose.' · '.$website,
                ];
            })
            ->values()
            ->all();
    }

    /**
     * @return list<array{value: string, label: string}>
     */
    private function websitesFor(User $user, string $purpose): array
    {
        return $user->promptLandingPages()
            ->forPurposeTab($purpose)
            ->whereNotNull('website_url')
            ->where('website_url', '!=', '')
            ->distinct()
            ->orderBy('website_url')
            ->pluck('website_url')
            ->map(fn (string $url): array => [
                'value' => $url,
                'label' => parse_url($url, PHP_URL_HOST) ?: $url,
            ])
            ->values()
            ->all();
    }

    private function assertPendingApproval(PromptLandingPage $page): void
    {
        if ($page->canApprove()) {
            return;
        }

        throw ValidationException::withMessages([
            'status' => 'This landing page is not waiting for approval.',
        ]);
    }

    private function ownedPage(Request $request, int $id): PromptLandingPage
    {
        $page = PromptLandingPage::query()->find($id);

        abort_if(
            $page === null || $page->user_id !== $request->user()?->id,
            404,
        );

        return $page;
    }

    /**
     * @param  array{message?: ?string}  $validated
     */
    private function promptFor(array $validated, Request $request): string
    {
        $message = trim((string) ($validated['message'] ?? ''));

        if ($message !== '') {
            return $message;
        }

        $count = count($request->file('files', []));

        return $count === 1
            ? 'I have attached 1 file.'
            : "I have attached {$count} files.";
    }

    private function storeAttachment(
        PromptLandingPage $page,
        UploadedFile $file,
        PromptLandingPageMessage $message,
    ): void {
        $mime = (string) $file->getMimeType();
        $kind = str_starts_with($mime, 'image/') ? 'image' : 'document';

        $page->attachments()->create([
            'prompt_landing_page_message_id' => $message->id,
            'path' => $file->store('prompt-landing-pages/'.$page->id),
            'original_name' => $file->getClientOriginalName(),
            'mime' => $mime,
            'kind' => $kind,
            'included_in_brief' => false,
        ]);
    }

    /**
     * @return list<array{role: string, text: string, html: ?string, attachments: list<array{name: string, kind: string, badge: string, preview: string, url: string, download_url: string}>}>
     */
    private function transcript(PromptLandingPage $page): array
    {
        return $page->messages
            ->filter(fn ($message): bool => trim($message->content) !== '' || $message->attachments->isNotEmpty())
            ->map(fn ($message): array => [
                'role' => $message->role === 'user' ? 'user' : 'agent',
                'text' => $message->content,
                'html' => $message->role === 'user' ? null : (string) Markdown::toHtml($message->content),
                'attachments' => $message->attachments
                    ->map(fn ($file): array => [
                        'name' => $file->original_name,
                        'kind' => $file->kind,
                        'badge' => $file->badge(),
                        'preview' => $file->previewKind(),
                        'url' => route('prompt-landing-pages.attachments.show', [$page, $file]),
                        'download_url' => route('prompt-landing-pages.attachments.show', [
                            'page' => $page,
                            'attachment' => $file,
                            'download' => 1,
                        ]),
                    ])
                    ->values()
                    ->all(),
            ])
            ->values()
            ->all();
    }

    private function inlineMime(PromptLandingPageAttachment $attachment): string
    {
        $mime = strtolower((string) $attachment->mime);

        if ($mime === '' || str_contains($mime, 'html') || str_contains($mime, 'javascript')) {
            return 'application/octet-stream';
        }

        return (string) $attachment->mime;
    }

    private function payload(PromptLandingPage $page): array
    {
        $page->promoteLegacyCompleted();

        $done = ! $page->isBusy();
        $latestAssistant = $done
            ? $page->messages
                ->reverse()
                ->first(fn ($message) => $message->role === 'assistant')
            : null;

        $reply = $latestAssistant?->content
            ?? ($page->status === 'failed' ? (string) $page->failure_reason : '');

        return [
            'conversation' => $page->id,
            'conversation_title' => $page->displayTitle(),
            'page_id' => $page->id,
            'status' => $page->status,
            'status_label' => $page->statusLabel(),
            'done' => $done,
            'reply' => $reply,
            'replyHtml' => $reply !== '' ? (string) Markdown::toHtml($reply) : '',
            'preview_url' => $page->isReady() ? route('prompt-landing-pages.preview', $page) : null,
            'download_url' => $page->canDownload() ? route('prompt-landing-pages.download', $page) : null,
            'editor_url' => $page->canEdit() ? route('prompt-landing-pages.editor', $page) : null,
            'partner_preview_url' => $page->partnerPreviewUrl(),
            'approve_url' => route('prompt-landing-pages.approve', $page),
            'reject_url' => route('prompt-landing-pages.reject', $page),
            'can_approve' => $page->canApprove(),
            'approved' => $page->isApproved(),
            'rejected' => $page->isRejected(),
            'composer_locked' => $page->composerLocked(),
            'status_url' => route('prompt-landing-pages.status', $page),
            'show_url' => route('prompt-landing-pages.show', $page),
            'failure_reason' => $page->failure_reason,
            'setup_complete' => $page->hasSetup(),
            'website_url' => $page->website_url,
            'purpose' => $page->purpose,
            'public_url' => $page->publicUrl(),
            'leads_url' => route('prompt-landing-pages.leads.index', $page),
            'leads_count' => $page->isApproved() ? $page->leads()->submitted()->count() : 0,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function setupPayload(PromptLandingPage $page): array
    {
        return [
            'conversation' => $page->id,
            'conversation_title' => $page->displayTitle(),
            'page_id' => $page->id,
            'status' => $page->status,
            'setup_complete' => $page->hasSetup(),
            'website_url' => $page->website_url,
            'purpose' => $page->purpose,
            'show_url' => route('prompt-landing-pages.show', $page),
        ];
    }

    private function recentCampaignConversations(Request $request)
    {
        return Conversation::query()
            ->where('participant_type', $request->user()?->getMorphClass())
            ->where('participant_id', $request->user()?->getKey())
            ->latest('updated_at')
            ->limit(30)
            ->get();
    }
}
