<?php

declare(strict_types=1);

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Models\World;
use App\Models\WorldWebStory;
use App\Models\WorldWebStoryPage;
use App\Services\WebStories\WorldWebStoryAssetService;
use App\Services\WebStories\WorldWebStoryGenerator;
use App\Services\WebStories\WorldWebStoryValidationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;

final class WorldWebStoryAdminController extends Controller
{
    private const PER_PAGE = 20;

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

    public function index(Request $request): Response
    {
        $filters = [
            'q' => trim((string) $request->query('q', '')),
            'status' => trim((string) $request->query('status', 'all')),
        ];

        $stories = WorldWebStory::query()
            ->with('world')
            ->when($filters['q'] !== '', function ($query) use ($filters): void {
                $query->where(function ($nested) use ($filters): void {
                    $nested->where('title', 'like', '%' . $filters['q'] . '%')
                        ->orWhere('slug', 'like', '%' . $filters['q'] . '%')
                        ->orWhereHas('world', fn ($worldQuery) => $worldQuery->where('title', 'like', '%' . $filters['q'] . '%')->orWhere('slug', 'like', '%' . $filters['q'] . '%'));
                });
            })
            ->when($filters['status'] !== 'all', fn ($query) => $query->where('status', $filters['status']))
            ->orderByDesc('published_at')
            ->orderByDesc('updated_at')
            ->paginate(self::PER_PAGE)
            ->withQueryString()
            ->through(fn (WorldWebStory $story): array => $this->mapStoryListItem($story));

        return Inertia::render('Moderation/WorldWebStoriesIndex', [
            'title' => 'World Web Stories',
            'stories' => $stories,
            'filters' => $filters,
            'stats' => [
                'total' => WorldWebStory::query()->count(),
                'published' => WorldWebStory::query()->where('status', WorldWebStory::STATUS_PUBLISHED)->count(),
                'draft' => WorldWebStory::query()->where('status', WorldWebStory::STATUS_DRAFT)->count(),
                'hidden' => WorldWebStory::query()->where('noindex', true)->orWhere('active', false)->count(),
            ],
            'worldOptions' => $this->worldOptions(),
            'endpoints' => [
                'index' => route('admin.web-stories.index'),
                'create' => route('admin.web-stories.create'),
                'editPattern' => route('admin.web-stories.edit', ['story' => '__STORY__']),
                'destroyPattern' => route('admin.web-stories.destroy', ['story' => '__STORY__']),
                'publishPattern' => route('admin.web-stories.publish', ['story' => '__STORY__']),
                'unpublishPattern' => route('admin.web-stories.unpublish', ['story' => '__STORY__']),
                'generatePattern' => route('admin.web-stories.generate', ['world' => '__WORLD__']),
            ],
        ])->rootView('moderation');
    }

    public function create(): Response
    {
        return Inertia::render('Moderation/WorldWebStoryEditor', [
            'story' => $this->blankStoryPayload(),
            'worldOptions' => $this->worldOptions(),
            'endpoints' => $this->editorEndpoints(),
            'isNew' => true,
        ])->rootView('moderation');
    }

    public function store(Request $request): RedirectResponse
    {
        $attributes = $this->validatedStoryAttributes($request);
        $story = new WorldWebStory();
        $story->fill($attributes + [
            'created_by' => (int) $request->user()->id,
            'updated_by' => (int) $request->user()->id,
        ]);
        $this->normalizeStatusTimestamps($story);
        $this->assertPublishedStateIsValid($story);
        $story->save();

        return redirect()->route('admin.web-stories.edit', ['story' => $story])->with('success', 'Web story created.');
    }

    public function edit(WorldWebStory $story): Response
    {
        $story->load(['world', 'orderedPages.artwork']);

        return Inertia::render('Moderation/WorldWebStoryEditor', [
            'story' => $this->mapStoryEditorPayload($story),
            'worldOptions' => $this->worldOptions(),
            'endpoints' => $this->editorEndpoints($story),
            'isNew' => false,
        ])->rootView('moderation');
    }

    public function update(Request $request, WorldWebStory $story): RedirectResponse
    {
        $story->fill($this->validatedStoryAttributes($request) + [
            'updated_by' => (int) $request->user()->id,
        ]);
        $this->normalizeStatusTimestamps($story);
        $this->assertPublishedStateIsValid($story);
        $story->save();

        return back()->with('success', 'Web story updated.');
    }

    public function destroy(WorldWebStory $story): JsonResponse
    {
        $story->delete();

        return response()->json([
            'ok' => true,
            'message' => 'Web story deleted.',
        ]);
    }

    public function storePage(Request $request, WorldWebStory $story): JsonResponse
    {
        $attributes = $this->validatedPageAttributes($request, $story, null);
        $page = $story->pages()->create($attributes);

        return response()->json([
            'ok' => true,
            'message' => 'Page created.',
            'page' => $this->mapPage($page->fresh('artwork')),
        ]);
    }

    public function updatePage(Request $request, WorldWebStory $story, WorldWebStoryPage $page): JsonResponse
    {
        abort_unless((int) $page->story_id === (int) $story->id, 404);

        $page->fill($this->validatedPageAttributes($request, $story, $page));
        $page->save();

        return response()->json([
            'ok' => true,
            'message' => 'Page updated.',
            'page' => $this->mapPage($page->fresh('artwork')),
        ]);
    }

    public function destroyPage(WorldWebStory $story, WorldWebStoryPage $page): JsonResponse
    {
        abort_unless((int) $page->story_id === (int) $story->id, 404);

        $page->delete();

        return response()->json([
            'ok' => true,
            'message' => 'Page deleted.',
        ]);
    }

    public function reorderPages(Request $request, WorldWebStory $story): JsonResponse
    {
        $validated = $request->validate([
            'page_ids' => ['required', 'array', 'min:1'],
            'page_ids.*' => ['integer'],
        ]);

        $ids = collect($validated['page_ids'])->map(fn ($id): int => (int) $id)->values();
        $pages = $story->orderedPages()->whereIn('id', $ids)->get()->keyBy('id');

        abort_unless($pages->count() === $ids->count(), 422);

        foreach ($ids as $index => $id) {
            $pages[$id]->forceFill(['position' => $index + 1])->save();
        }

        return response()->json([
            'ok' => true,
            'message' => 'Page order updated.',
        ]);
    }

    public function generateFromWorld(Request $request, World $world): JsonResponse
    {
        $validated = $request->validate([
            'force' => ['nullable', 'boolean'],
            'publish' => ['nullable', 'boolean'],
            'dry_run' => ['nullable', 'boolean'],
            'pages' => ['nullable', 'integer', 'min:5', 'max:10'],
        ]);

        $result = $this->generator->generateFromWorld(
            $world,
            $request->user(),
            (int) ($validated['pages'] ?? 7),
            (bool) ($validated['force'] ?? false),
            (bool) ($validated['publish'] ?? false),
            (bool) ($validated['dry_run'] ?? false),
        );

        return response()->json([
            'ok' => true,
            'message' => $result['created'] ? 'Web story draft generated.' : 'Web story draft regenerated.',
            'story' => [
                'id' => $result['story']->id,
                'slug' => $result['story']->slug,
                'edit_url' => $result['story']->exists ? route('admin.web-stories.edit', ['story' => $result['story']->id]) : null,
            ],
            'validation' => $result['validation'],
        ]);
    }

    public function publish(WorldWebStory $story): JsonResponse
    {
        $this->assets->buildAssets($story, force: false);
        $story->refresh()->load('orderedPages');
        $this->validation->assertPublishable($story);
        $story->forceFill([
            'status' => WorldWebStory::STATUS_PUBLISHED,
            'published_at' => $story->published_at ?: now(),
        ])->save();

        return response()->json([
            'ok' => true,
            'message' => 'Web story published.',
        ]);
    }

    public function unpublish(WorldWebStory $story): JsonResponse
    {
        $story->forceFill([
            'status' => WorldWebStory::STATUS_DRAFT,
            'published_at' => null,
        ])->save();

        return response()->json([
            'ok' => true,
            'message' => 'Web story reverted to draft.',
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedStoryAttributes(Request $request, ?WorldWebStory $story = null): array
    {
        $validated = $request->validate([
            'world_id' => ['nullable', 'integer', Rule::exists('worlds', 'id')],
            'slug' => ['required', 'string', 'max:120', Rule::unique('world_web_stories', 'slug')->ignore($story?->id)],
            'title' => ['required', 'string', 'max:255'],
            'subtitle' => ['nullable', 'string', 'max:255'],
            'excerpt' => ['nullable', 'string', 'max:400'],
            'description' => ['nullable', 'string', 'max:2000'],
            'seo_title' => ['nullable', 'string', 'max:255'],
            'seo_description' => ['nullable', 'string', 'max:400'],
            'poster_portrait_path' => ['nullable', 'string', 'max:2048'],
            'poster_square_path' => ['nullable', 'string', 'max:2048'],
            'publisher_logo_path' => ['nullable', 'string', 'max:2048'],
            'status' => ['required', Rule::in([WorldWebStory::STATUS_DRAFT, WorldWebStory::STATUS_PUBLISHED, WorldWebStory::STATUS_ARCHIVED])],
            'featured' => ['required', 'boolean'],
            'active' => ['required', 'boolean'],
            'noindex' => ['required', 'boolean'],
            'published_at' => ['nullable', 'date'],
            'starts_at' => ['nullable', 'date'],
            'ends_at' => ['nullable', 'date', 'after_or_equal:starts_at'],
        ]);

        return $validated;
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedPageAttributes(Request $request, WorldWebStory $story, ?WorldWebStoryPage $page): array
    {
        $validated = $request->validate([
            'artwork_id' => ['nullable', 'integer', Rule::exists('artworks', 'id')],
            'position' => ['nullable', 'integer', 'min:1'],
            'layout' => ['required', Rule::in([
                WorldWebStoryPage::LAYOUT_COVER,
                WorldWebStoryPage::LAYOUT_ARTWORK,
                WorldWebStoryPage::LAYOUT_CREATOR,
                WorldWebStoryPage::LAYOUT_MOOD,
                WorldWebStoryPage::LAYOUT_COLLECTION,
                WorldWebStoryPage::LAYOUT_CTA,
            ])],
            'background_type' => ['required', Rule::in([
                WorldWebStoryPage::BACKGROUND_IMAGE,
                WorldWebStoryPage::BACKGROUND_VIDEO,
                WorldWebStoryPage::BACKGROUND_GRADIENT,
            ])],
            'background_path' => ['nullable', 'string', 'max:2048'],
            'background_mobile_path' => ['nullable', 'string', 'max:2048'],
            'headline' => ['nullable', 'string', 'max:255'],
            'body' => ['nullable', 'string', 'max:180'],
            'cta_label' => ['nullable', 'string', 'max:120'],
            'cta_url' => ['nullable', 'string', 'max:2048'],
            'alt_text' => ['required', 'string', 'max:255'],
            'caption' => ['nullable', 'string', 'max:120'],
            'credit_text' => ['nullable', 'string', 'max:255'],
            'text_position' => ['required', Rule::in(['top', 'center', 'bottom'])],
            'overlay_strength' => ['required', 'integer', 'min:0', 'max:100'],
            'animation' => ['nullable', Rule::in(['fade-in', 'fly-in-bottom', 'pulse', 'pan-left', 'pan-right'])],
            'active' => ['required', 'boolean'],
        ]);

        $validated['position'] = (int) ($validated['position'] ?? ($story->orderedPages()->max('position') + ($page ? 0 : 1) ?: 1));
        $pageErrors = $this->validation->validatePagePayload($validated);

        if ($pageErrors !== []) {
            throw ValidationException::withMessages($pageErrors);
        }

        return $validated;
    }

    private function normalizeStatusTimestamps(WorldWebStory $story): void
    {
        if ((string) $story->status === WorldWebStory::STATUS_PUBLISHED && $story->published_at === null) {
            $story->published_at = now();
        }

        if ((string) $story->status === WorldWebStory::STATUS_DRAFT) {
            $story->published_at = null;
        }
    }

    private function assertPublishedStateIsValid(WorldWebStory $story): void
    {
        if ((string) $story->status !== WorldWebStory::STATUS_PUBLISHED) {
            return;
        }

        $story->loadMissing('orderedPages');
        $this->validation->assertPublishable($story);
    }

    /**
     * @return array<int, array{value:int,label:string,description:string}>
     */
    private function worldOptions(): array
    {
        return World::query()
            ->orderByDesc('published_at')
            ->orderBy('title')
            ->limit(200)
            ->get(['id', 'title', 'slug'])
            ->map(fn (World $world): array => [
                'value' => (int) $world->id,
                'label' => (string) $world->title,
                'description' => (string) $world->slug,
            ])
            ->all();
    }

    /**
     * @return array<string, mixed>
     */
    private function blankStoryPayload(): array
    {
        return [
            'id' => null,
            'world_id' => null,
            'slug' => '',
            'title' => '',
            'subtitle' => '',
            'excerpt' => '',
            'description' => '',
            'seo_title' => '',
            'seo_description' => '',
            'poster_portrait_path' => '',
            'poster_square_path' => '',
            'publisher_logo_path' => $this->assets->defaultPublisherLogoPath(),
            'status' => WorldWebStory::STATUS_DRAFT,
            'featured' => false,
            'active' => true,
            'noindex' => false,
            'published_at' => null,
            'starts_at' => null,
            'ends_at' => null,
            'world' => null,
            'pages' => [],
            'public_url' => null,
            'validation' => ['valid' => false, 'errors' => [], 'warnings' => [], 'page_count' => 0],
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function mapStoryEditorPayload(WorldWebStory $story): array
    {
        return [
            'id' => (int) $story->id,
            'world_id' => $story->world_id ? (int) $story->world_id : null,
            'slug' => (string) $story->slug,
            'title' => (string) $story->title,
            'subtitle' => (string) ($story->subtitle ?? ''),
            'excerpt' => (string) ($story->excerpt ?? ''),
            'description' => (string) ($story->description ?? ''),
            'seo_title' => (string) ($story->seo_title ?? ''),
            'seo_description' => (string) ($story->seo_description ?? ''),
            'poster_portrait_path' => (string) ($story->poster_portrait_path ?? ''),
            'poster_square_path' => (string) ($story->poster_square_path ?? ''),
            'publisher_logo_path' => (string) ($story->publisher_logo_path ?? ''),
            'status' => (string) $story->status,
            'featured' => (bool) $story->featured,
            'active' => (bool) $story->active,
            'noindex' => (bool) $story->noindex,
            'published_at' => optional($story->published_at)?->toIso8601String(),
            'starts_at' => optional($story->starts_at)?->toIso8601String(),
            'ends_at' => optional($story->ends_at)?->toIso8601String(),
            'world' => $story->world ? [
                'id' => (int) $story->world->id,
                'title' => (string) $story->world->title,
                'slug' => (string) $story->world->slug,
            ] : null,
            'pages' => $story->orderedPages->map(fn (WorldWebStoryPage $page): array => $this->mapPage($page))->all(),
            'public_url' => route('web-stories.show', ['slug' => $story->slug]),
            'validation' => $this->validation->validate($story),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function mapStoryListItem(WorldWebStory $story): array
    {
        return [
            'id' => (int) $story->id,
            'slug' => (string) $story->slug,
            'title' => (string) $story->title,
            'excerpt' => (string) ($story->excerpt ?? ''),
            'status' => (string) $story->status,
            'active' => (bool) $story->active,
            'noindex' => (bool) $story->noindex,
            'featured' => (bool) $story->featured,
            'page_count' => (int) ($story->pages()->count()),
            'published_at' => optional($story->published_at)?->toIso8601String(),
            'poster_portrait_url' => $story->posterPortraitUrl(),
            'world' => $story->world ? [
                'id' => (int) $story->world->id,
                'title' => (string) $story->world->title,
                'slug' => (string) $story->world->slug,
            ] : null,
            'public_url' => route('web-stories.show', ['slug' => $story->slug]),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function mapPage(WorldWebStoryPage $page): array
    {
        return [
            'id' => (int) $page->id,
            'artwork_id' => $page->artwork_id ? (int) $page->artwork_id : null,
            'position' => (int) $page->position,
            'layout' => (string) $page->layout,
            'background_type' => (string) $page->background_type,
            'background_path' => (string) ($page->background_path ?? ''),
            'background_mobile_path' => (string) ($page->background_mobile_path ?? ''),
            'headline' => (string) ($page->headline ?? ''),
            'body' => (string) ($page->body ?? ''),
            'cta_label' => (string) ($page->cta_label ?? ''),
            'cta_url' => (string) ($page->cta_url ?? ''),
            'alt_text' => (string) ($page->alt_text ?? ''),
            'caption' => (string) ($page->caption ?? ''),
            'credit_text' => (string) ($page->credit_text ?? ''),
            'text_position' => (string) ($page->text_position ?? 'bottom'),
            'overlay_strength' => (int) ($page->overlay_strength ?? 35),
            'animation' => (string) ($page->animation ?? ''),
            'active' => (bool) $page->active,
            'background_url' => $page->backgroundUrl(),
        ];
    }

    /**
     * @return array<string, string>
     */
    private function editorEndpoints(?WorldWebStory $story = null): array
    {
        return [
            'store' => route('admin.web-stories.store'),
            'update' => $story ? route('admin.web-stories.update', ['story' => $story]) : '',
            'destroy' => $story ? route('admin.web-stories.destroy', ['story' => $story]) : '',
            'pagesStore' => $story ? route('admin.web-stories.pages.store', ['story' => $story]) : '',
            'pagesUpdatePattern' => $story ? route('admin.web-stories.pages.update', ['story' => $story, 'page' => '__PAGE__']) : '',
            'pagesDestroyPattern' => $story ? route('admin.web-stories.pages.destroy', ['story' => $story, 'page' => '__PAGE__']) : '',
            'pagesReorder' => $story ? route('admin.web-stories.pages.reorder', ['story' => $story]) : '',
            'publish' => $story ? route('admin.web-stories.publish', ['story' => $story]) : '',
            'unpublish' => $story ? route('admin.web-stories.unpublish', ['story' => $story]) : '',
            'generateFromWorldPattern' => route('admin.web-stories.generate', ['world' => '__WORLD__']),
            'index' => route('admin.web-stories.index'),
        ];
    }
}