<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\WorldWebStory;
use App\Services\WebStories\WorldWebStoryValidationService;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;

final class ValidateWorldWebStoriesCommand extends Command
{
    protected $signature = 'skinbase:webstories:validate
        {story? : Story ID or slug}
        {--published : Limit batch mode to published stories}
        {--visible : Limit batch mode to publicly visible stories}
        {--limit=100 : Maximum stories to validate in batch mode}
        {--amp : Also run amphtml-validator against the public story URL}
        {--fail-warnings : Treat validation warnings as failures}';

    protected $description = 'Validate World Web Stories for publish safety and optional AMP validity';

    public function handle(WorldWebStoryValidationService $validation): int
    {
        $storyKey = $this->argument('story');

        if ($storyKey !== null && trim((string) $storyKey) !== '') {
            $story = $this->resolveStory((string) $storyKey);

            if (! $story instanceof WorldWebStory) {
                $this->error(sprintf('Web story [%s] was not found.', (string) $storyKey));

                return self::FAILURE;
            }

            return $this->validateOne($validation, $story);
        }

        return $this->validateBatch($validation, max(1, (int) $this->option('limit')));
    }

    private function validateOne(WorldWebStoryValidationService $validation, WorldWebStory $story): int
    {
        $result = $validation->validate($story);
        $ampErrors = $this->ampErrors($story);

        $this->line(sprintf('Story [%d] %s', (int) $story->id, (string) $story->slug));

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

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

        foreach ($ampErrors as $ampError) {
            $this->error(' - AMP: ' . $ampError);
        }

        if ($result['valid'] && $ampErrors === []) {
            $this->info('Validation passed.');

            return self::SUCCESS;
        }

        return self::FAILURE;
    }

    private function validateBatch(WorldWebStoryValidationService $validation, int $limit): int
    {
        $processed = 0;
        $failed = 0;

        $this->storyQuery()
            ->limit($limit)
            ->get()
            ->each(function (WorldWebStory $story) use ($validation, &$processed, &$failed): void {
                $processed++;
                $result = $validation->validate($story);
                $ampErrors = $this->ampErrors($story);
                $warningsFail = (bool) $this->option('fail-warnings') && count((array) $result['warnings']) > 0;
                $hasFailure = ! $result['valid'] || $warningsFail || $ampErrors !== [];

                if ($hasFailure) {
                    $failed++;
                }

                $this->line(sprintf('[%d] %s -> %s', (int) $story->id, (string) $story->slug, $hasFailure ? 'invalid' : 'valid'));

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

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

                foreach ($ampErrors as $ampError) {
                    $this->error(' - AMP: ' . $ampError);
                }
            });

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

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

    private function storyQuery()
    {
        return WorldWebStory::query()
            ->when((bool) $this->option('published'), fn ($query) => $query->published())
            ->when((bool) $this->option('visible'), fn ($query) => $query->visible())
            ->orderByDesc('published_at')
            ->orderByDesc('id');
    }

    /**
     * @return list<string>
     */
    private function ampErrors(WorldWebStory $story): array
    {
        if (! (bool) $this->option('amp')) {
            return [];
        }

        if (! $story->exists || ! $story->publicUrl()) {
            return ['Story has no public URL to validate.'];
        }

        $probe = new Process(['npx', 'amphtml-validator', '--version'], base_path(), null, null, 60);
        $probe->run();

        if (! $probe->isSuccessful()) {
            return ['amphtml-validator is not available via npx.'];
        }

        $process = new Process(['npx', 'amphtml-validator', $story->publicUrl()], base_path(), null, null, 120);
        $process->run();

        if ($process->isSuccessful()) {
            return [];
        }

        $output = trim($process->getErrorOutput() ?: $process->getOutput());

        if ($output === '') {
            return ['AMP validator failed without output.'];
        }

        $lines = preg_split('/\r\n|\r|\n/', $output);

        return $lines === false || $lines === [] ? ['AMP validator failed.'] : $lines;
    }

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