<?php

declare(strict_types=1);

namespace App\Services\WebStories;

use App\Models\Artwork;
use App\Models\User;
use App\Models\World;
use App\Models\WorldSubmission;
use App\Models\WorldWebStory;
use App\Models\WorldWebStoryPage;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

final class WorldWebStoryGenerator
{
    public function __construct(
        private readonly WorldWebStoryAssetService $assets,
        private readonly WorldWebStoryValidationService $validation,
    ) {
    }

    /**
     * @return array{story: WorldWebStory, created: bool, validation: array{valid: bool, errors: list<string>, warnings: list<string>, page_count: int}}
     */
    public function generateFromWorld(World $world, ?User $actor = null, int $pages = 7, bool $force = false, bool $publish = false, bool $dryRun = false): array
    {
        $pageCount = max(5, min(10, $pages));
        $existing = WorldWebStory::query()->where('world_id', $world->id)->orderByDesc('id')->first();

        if ($existing && ! $force && ! $dryRun) {
            throw ValidationException::withMessages([
                'world' => ['A web story already exists for this world. Use --force to rebuild it.'],
            ]);
        }

        $selectedArtworks = $this->candidateArtworks($world)->take(max(3, $pageCount - 3))->values();
        $storyAttributes = [
            'world_id' => $world->id,
            'slug' => $existing?->slug ?: $this->uniqueSlug($world->slug, $existing?->id),
            'title' => $existing?->title ?: (string) $world->title,
            'subtitle' => $world->tagline,
            'excerpt' => $world->summary ?: $world->tagline,
            'description' => $world->description ?: $world->summary,
            'seo_title' => trim((string) ($world->seo_title ?: ($world->title . ' – Skinbase Web Story'))),
            'seo_description' => trim((string) ($world->seo_description ?: $world->summary ?: $world->description ?: '')),
            'status' => WorldWebStory::STATUS_DRAFT,
            'active' => true,
            'noindex' => false,
            'featured' => false,
            'updated_by' => $actor?->id,
        ];

        if (! $existing) {
            $storyAttributes['created_by'] = $actor?->id;
        }

        $pagePayloads = $this->buildPagePayloads($world, $selectedArtworks, $pageCount);

        if ($dryRun) {
            $story = $existing ?? new WorldWebStory($storyAttributes);
            $story->fill($storyAttributes);
            $story->setRelation('orderedPages', collect($pagePayloads)->map(fn (array $page): WorldWebStoryPage => new WorldWebStoryPage($page)));

            $this->assets->buildAssets($story, force: $force, dryRun: true);
            $validation = $this->validation->validate($story);

            return [
                'story' => $story,
                'created' => ! $existing,
                'validation' => $validation,
            ];
        }

        $story = DB::transaction(function () use ($existing, $storyAttributes, $pagePayloads): WorldWebStory {
            $story = $existing ?? new WorldWebStory();
            $story->fill($storyAttributes);
            $story->save();

            $story->pages()->delete();

            foreach ($pagePayloads as $pagePayload) {
                $story->pages()->create($pagePayload);
            }

            return $story->fresh(['orderedPages', 'world']);
        });

        $this->assets->buildAssets($story, force: $force);
        $story->refresh()->load('orderedPages', 'world');

        if ($publish) {
            $this->validation->assertPublishable($story);
            $story->forceFill([
                'status' => WorldWebStory::STATUS_PUBLISHED,
                'published_at' => now(),
            ])->save();
        }

        return [
            'story' => $story->fresh(['orderedPages', 'world']),
            'created' => ! $existing,
            'validation' => $this->validation->validate($story),
        ];
    }

    /**
     * @return Collection<int, Artwork>
     */
    private function candidateArtworks(World $world): Collection
    {
        $relationIds = $world->worldRelations()
            ->where('related_type', 'artwork')
            ->orderByDesc('is_featured')
            ->orderBy('sort_order')
            ->pluck('related_id')
            ->map(fn ($id): int => (int) $id)
            ->filter()
            ->values();

        $artworks = collect();

        if ($relationIds->isNotEmpty()) {
            $artworks = Artwork::query()
                ->whereIn('id', $relationIds)
                ->get()
                ->sortBy(fn (Artwork $artwork): int => $relationIds->search((int) $artwork->id))
                ->values();
        }

        if ($artworks->count() < 3) {
            $submissionArtworks = WorldSubmission::query()
                ->with('artwork.user')
                ->where('world_id', $world->id)
                ->where('status', WorldSubmission::STATUS_LIVE)
                ->orderByDesc('is_featured')
                ->orderByDesc('featured_at')
                ->orderByDesc('id')
                ->get()
                ->pluck('artwork')
                ->filter(fn ($artwork): bool => $artwork instanceof Artwork);

            $artworks = $artworks->concat($submissionArtworks)->unique(fn (Artwork $artwork): int => (int) $artwork->id)->values();
        }

        return $artworks;
    }

    /**
     * @param  Collection<int, Artwork>  $artworks
     * @return list<array<string, mixed>>
     */
    private function buildPagePayloads(World $world, Collection $artworks, int $pageCount): array
    {
        $primaryArtwork = $artworks->get(0);
        $secondaryArtwork = $artworks->get(1) ?: $primaryArtwork;
        $tertiaryArtwork = $artworks->get(2) ?: $secondaryArtwork;

        $pages = [
            [
                'position' => 1,
                'layout' => WorldWebStoryPage::LAYOUT_COVER,
                'background_type' => WorldWebStoryPage::BACKGROUND_IMAGE,
                'headline' => (string) $world->title,
                'body' => Str::limit((string) ($world->tagline ?: $world->summary ?: 'A cinematic Skinbase World.'), 160, ''),
                'caption' => 'Skinbase World',
                'alt_text' => (string) $world->title,
                'text_position' => 'bottom',
                'overlay_strength' => 45,
                'animation' => 'fade-in',
                'active' => true,
            ],
            [
                'position' => 2,
                'layout' => WorldWebStoryPage::LAYOUT_MOOD,
                'background_type' => WorldWebStoryPage::BACKGROUND_IMAGE,
                'headline' => 'Step into ' . $world->title,
                'body' => Str::limit((string) ($world->summary ?: $world->description ?: 'Curated visuals, featured creators, and a clear editorial mood.'), 170, ''),
                'caption' => 'World intro',
                'alt_text' => 'Intro for ' . $world->title,
                'text_position' => 'bottom',
                'overlay_strength' => 35,
                'animation' => 'fly-in-bottom',
                'active' => true,
            ],
        ];

        if ($primaryArtwork instanceof Artwork) {
            $pages[] = [
                'position' => count($pages) + 1,
                'layout' => WorldWebStoryPage::LAYOUT_ARTWORK,
                'artwork_id' => $primaryArtwork->id,
                'background_type' => WorldWebStoryPage::BACKGROUND_IMAGE,
                'headline' => (string) ($primaryArtwork->title ?: 'Featured artwork'),
                'body' => Str::limit('A featured visual from ' . $world->title . ' by ' . ($primaryArtwork->user?->name ?: $primaryArtwork->user?->username ?: 'a Skinbase creator') . '.', 160, ''),
                'caption' => 'Featured artwork',
                'alt_text' => (string) ($primaryArtwork->title ?: 'Featured artwork'),
                'text_position' => 'bottom',
                'overlay_strength' => 35,
                'animation' => 'pan-left',
                'active' => true,
            ];
        }

        if ($secondaryArtwork instanceof Artwork) {
            $pages[] = [
                'position' => count($pages) + 1,
                'layout' => WorldWebStoryPage::LAYOUT_CREATOR,
                'artwork_id' => $secondaryArtwork->id,
                'background_type' => WorldWebStoryPage::BACKGROUND_IMAGE,
                'headline' => 'Creator spotlight',
                'body' => Str::limit(($secondaryArtwork->user?->name ?: $secondaryArtwork->user?->username ?: 'A featured creator') . ' helps define the mood of ' . $world->title . '.', 160, ''),
                'caption' => 'Creator spotlight',
                'alt_text' => (string) ($secondaryArtwork->title ?: 'Creator spotlight artwork'),
                'text_position' => 'bottom',
                'overlay_strength' => 40,
                'animation' => 'fade-in',
                'active' => true,
            ];
        }

        if ($tertiaryArtwork instanceof Artwork) {
            $pages[] = [
                'position' => count($pages) + 1,
                'layout' => WorldWebStoryPage::LAYOUT_COLLECTION,
                'artwork_id' => $tertiaryArtwork->id,
                'background_type' => WorldWebStoryPage::BACKGROUND_IMAGE,
                'headline' => 'More from this World',
                'body' => Str::limit('Explore more wallpapers, digital art, and creator picks collected inside ' . $world->title . '.', 155, ''),
                'caption' => 'Community picks',
                'alt_text' => (string) ($tertiaryArtwork->title ?: 'World picks'),
                'text_position' => 'bottom',
                'overlay_strength' => 35,
                'animation' => 'pan-right',
                'active' => true,
            ];
        }

        while (count($pages) < max(5, $pageCount - 1)) {
            $pages[] = [
                'position' => count($pages) + 1,
                'layout' => WorldWebStoryPage::LAYOUT_MOOD,
                'background_type' => WorldWebStoryPage::BACKGROUND_IMAGE,
                'headline' => 'Inside the theme',
                'body' => Str::limit('A short visual pause that keeps the story connected to ' . $world->title . '.', 150, ''),
                'caption' => 'World mood',
                'alt_text' => 'Mood page for ' . $world->title,
                'text_position' => 'bottom',
                'overlay_strength' => 35,
                'animation' => 'fade-in',
                'active' => true,
            ];
        }

        $pages[] = [
            'position' => count($pages) + 1,
            'layout' => WorldWebStoryPage::LAYOUT_CTA,
            'background_type' => WorldWebStoryPage::BACKGROUND_IMAGE,
            'headline' => 'Explore ' . $world->title,
            'body' => Str::limit('Open the full World page for the complete artwork grid, featured picks, and related creator content.', 160, ''),
            'caption' => 'Continue on Skinbase',
            'cta_label' => 'View World',
            'cta_url' => $world->publicUrl(),
            'alt_text' => 'Explore ' . $world->title . ' on Skinbase',
            'text_position' => 'bottom',
            'overlay_strength' => 45,
            'animation' => 'pulse',
            'active' => true,
        ];

        return collect($pages)
            ->take($pageCount)
            ->values()
            ->map(fn (array $page, int $index): array => array_merge($page, [
                'position' => $index + 1,
            ]))
            ->all();
    }

    private function uniqueSlug(string $base, ?int $ignoreId = null): string
    {
        $candidate = Str::slug($base) ?: 'web-story';
        $slug = $candidate;
        $suffix = 2;

        while (WorldWebStory::query()->when($ignoreId, fn ($query) => $query->whereKeyNot($ignoreId))->where('slug', $slug)->exists()) {
            $slug = $candidate . '-' . $suffix;
            $suffix++;
        }

        return $slug;
    }
}