<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\World;
use App\Services\WebStories\WorldWebStoryGenerator;
use Illuminate\Console\Command;
use Illuminate\Validation\ValidationException;

final class GenerateWorldWebStoriesCommand extends Command
{
    protected $signature = 'skinbase:webstories:generate
        {world? : World ID or slug}
        {--all : Generate stories in batch mode}
        {--pages=7 : Number of pages to generate (5-10)}
        {--limit=25 : Maximum worlds to process in batch mode}
        {--force : Rebuild an existing story for the target world}
        {--publish : Publish immediately after generation if validation passes}
        {--dry-run : Preview generation without saving anything}';

    protected $description = 'Generate standalone AMP Web Stories from Skinbase Worlds';

    public function handle(WorldWebStoryGenerator $generator): int
    {
        $worldKey = $this->argument('world');
        $force = (bool) $this->option('force');
        $publish = (bool) $this->option('publish');
        $dryRun = (bool) $this->option('dry-run');
        $pages = max(5, min(10, (int) $this->option('pages')));

        if ($worldKey !== null && trim((string) $worldKey) !== '') {
            $world = $this->resolveWorld((string) $worldKey);

            if (! $world instanceof World) {
                $this->error(sprintf('World [%s] was not found.', (string) $worldKey));

                return self::FAILURE;
            }

            return $this->generateOne($generator, $world, $pages, $force, $publish, $dryRun);
        }

        if (! (bool) $this->option('all')) {
            $this->error('Provide a world ID/slug or pass --all for batch generation.');

            return self::INVALID;
        }

        return $this->generateBatch($generator, $pages, $force, $publish, $dryRun, max(1, (int) $this->option('limit')));
    }

    private function generateOne(WorldWebStoryGenerator $generator, World $world, int $pages, bool $force, bool $publish, bool $dryRun): int
    {
        try {
            $result = $generator->generateFromWorld($world, null, $pages, $force, $publish, $dryRun);
        } catch (ValidationException $exception) {
            foreach ($exception->errors() as $messages) {
                foreach ($messages as $message) {
                    $this->error((string) $message);
                }
            }

            return self::FAILURE;
        }

        $story = $result['story'];
        $validation = $result['validation'];

        $this->info(sprintf(
            '%s story for world [%s] -> /web-stories/%s (%d pages)',
            $result['created'] ? 'Created' : 'Updated',
            (string) $world->slug,
            (string) $story->slug,
            (int) $validation['page_count'],
        ));

        foreach ((array) $validation['warnings'] as $warning) {
            $this->warn(' - ' . $warning);
        }

        foreach ((array) $validation['errors'] as $error) {
            $this->error(' - ' . $error);
        }

        return $validation['valid'] || ! $publish ? self::SUCCESS : self::FAILURE;
    }

    private function generateBatch(WorldWebStoryGenerator $generator, int $pages, bool $force, bool $publish, bool $dryRun, int $limit): int
    {
        $processed = 0;
        $created = 0;
        $updated = 0;
        $failed = 0;

        $query = World::query()
            ->published()
            ->orderByDesc('published_at')
            ->orderByDesc('id');

        if (! $force) {
            $query->whereDoesntHave('webStories');
        }

        $query->limit($limit)->get()->each(function (World $world) use ($generator, $pages, $force, $publish, $dryRun, &$processed, &$created, &$updated, &$failed): void {
            $processed++;

            try {
                $result = $generator->generateFromWorld($world, null, $pages, $force, $publish, $dryRun);
                $result['created'] ? $created++ : $updated++;
                $this->line(sprintf('[%d] %s -> %s', (int) $world->id, (string) $world->slug, (string) $result['story']->slug));
            } catch (ValidationException $exception) {
                $failed++;
                $first = collect($exception->errors())->flatten()->first();
                $this->error(sprintf('[%d] %s failed: %s', (int) $world->id, (string) $world->slug, (string) $first));
            }
        });

        $this->info(sprintf('Done. processed=%d created=%d updated=%d failed=%d', $processed, $created, $updated, $failed));

        return $failed === 0 ? self::SUCCESS : self::FAILURE;
    }

    private function resolveWorld(string $value): ?World
    {
        return World::query()
            ->when(is_numeric($value), fn ($query) => $query->where('id', (int) $value), fn ($query) => $query->where('slug', $value))
            ->first();
    }
}