<?php

namespace Database\Factories;

use App\Models\BlogPost;
use App\Models\User;
use Database\Factories\Concerns\CopiesSeederImages;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

/**
 * @extends Factory<BlogPost>
 */
class BlogPostFactory extends Factory
{
    use CopiesSeederImages;

    /**
     * Cached pool of pre-converted WebP featured images, built once per
     * process. The BlogPost featured_image mutator runs a GD JPEG->WebP
     * conversion on every non-WebP write (~165ms each); by converting the
     * source samples up front and reusing the resulting .webp paths, the
     * mutator's .webp early-return skips all per-row conversion - keeping
     * both seeding and the test suite fast.
     *
     * @var list<string>
     */
    private static array $featuredImagePool = [];

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        $title = fake()->sentence(rand(3, 8));

        return [
            'user_id' => User::factory(),
            'title' => $title,
            'slug' => Str::slug($title),
            'excerpt' => fake()->optional()->paragraph(),
            'content' => fake()->paragraphs(rand(5, 10), true),
            'featured_image' => $this->randomFeaturedImage(),
            'status' => 'draft',
            'published_at' => null,
        ];
    }

    /**
     * Return a featured image from the pre-converted WebP pool, falling back
     * to a plain copy when there are no source images to convert.
     */
    protected function randomFeaturedImage(): string
    {
        if (empty(self::$featuredImagePool)) {
            self::$featuredImagePool = $this->buildFeaturedImagePool();
        }

        if (empty(self::$featuredImagePool)) {
            return $this->copyRandomSeederImage('blog_samples', 'blog');
        }

        return fake()->randomElement(self::$featuredImagePool);
    }

    /**
     * Copy each blog sample image once and convert it to WebP via the
     * BlogPost model's own featured_image mutator, returning the resulting
     * WebP paths. Reusing the mutator keeps a single source of truth for the
     * conversion dimensions and quality (defined on the BlogPost model).
     *
     * @return list<string>
     */
    private function buildFeaturedImagePool(): array
    {
        $sourceDir = resource_path('seeder_images/blog_samples');
        $destinationDir = storage_path('app/public/blog');

        if (! File::exists($destinationDir)) {
            File::makeDirectory($destinationDir, 0755, true);
        }

        if (! File::exists($sourceDir)) {
            return [];
        }

        $images = File::files($sourceDir);

        if (empty($images)) {
            return [];
        }

        $webp = [];

        foreach ($images as $image) {
            $copiedName = uniqid().'.'.$image->getExtension();
            $copiedPath = "blog/{$copiedName}";

            File::copy($image->getPathname(), storage_path("app/public/{$copiedPath}"));

            // Setting the attribute runs the featured_image mutator, which
            // converts the copy to WebP, writes it to disk, and returns the
            // new .webp path. The model is never persisted here.
            $post = new BlogPost;
            $post->featured_image = $copiedPath;
            $webp[] = $post->featured_image;
        }

        return $webp;
    }

    /**
     * Indicate that the blog post is published.
     */
    public function published(): static
    {
        return $this->state(fn (array $attributes) => [
            'status' => 'published',
            'published_at' => fake()->dateTimeBetween('-1 year', 'now'),
        ]);
    }

    /**
     * Indicate that the blog post is archived.
     */
    public function archived(): static
    {
        return $this->state(fn (array $attributes) => [
            'status' => 'archived',
        ]);
    }

    /**
     * Indicate that the blog post is a draft.
     */
    public function draft(): static
    {
        return $this->state(fn (array $attributes) => [
            'status' => 'draft',
            'published_at' => null,
        ]);
    }
}
