<?php

namespace Database\Seeders;

use App\Models\Category;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

/**
 * Builds the 3-level category tree from database/seeders/data/taxonomy.json into
 * the self-referencing `categories` table (parent_id / level / path).
 *
 * Idempotent: matches existing nodes by (parent_id, name_en) so re-running keeps
 * the same ids. Globally-unique slugs are generated with a collision suffix.
 */
class CategorySeeder extends Seeder
{
    /** @var array<string,bool> slugs already taken this run */
    private array $usedSlugs = [];

    public function run(): void
    {
        $path = database_path('seeders/data/taxonomy.json');
        $menu = json_decode(File::get($path), true)['menu'] ?? [];

        // Pre-load existing slugs so re-runs don't collide with themselves.
        foreach (Category::pluck('slug') as $s) {
            $this->usedSlugs[$s] = true;
        }

        foreach ($menu as $ci => $cat) {
            $top = $this->upsert(null, 1, $cat['name'], $ci, $cat['icon'] ?? null);

            foreach ($cat['sub_categories'] as $si => $sub) {
                $node = $this->upsert($top, 2, $sub['name'], $si);

                $seen = [];
                $li = 0;
                foreach ($sub['items'] as $item) {
                    $key = Str::lower(trim($item));
                    if (isset($seen[$key])) {
                        continue; // skip exact duplicate leaves under the same parent
                    }
                    $seen[$key] = true;
                    $this->upsert($node, 3, $item, $li++);
                }
            }
        }

        $this->command?->info('Taxonomy seeded: ' . Category::count() . ' categories ('
            . Category::where('level', 1)->count() . ' top, '
            . Category::where('level', 2)->count() . ' sub, '
            . Category::where('level', 3)->count() . ' leaves).');
    }

    /**
     * Create/update one node and (re)compute its materialised path.
     */
    private function upsert(?Category $parent, int $level, string $name, int $sort, ?string $icon = null): Category
    {
        $node = Category::firstOrNew([
            'parent_id' => $parent?->id,
            'name_en'   => $name,
        ]);

        if (! $node->exists) {
            $node->slug = $this->uniqueSlug($parent, $name);
        }

        $node->fill([
            'level'      => $level,
            'icon'       => $icon ?? $node->icon,
            'is_active'  => true,
            'sort_order' => $sort,
        ])->save();

        $node->path = ($parent?->path ?? '/') . $node->id . '/';
        $node->saveQuietly();

        return $node;
    }

    private function uniqueSlug(?Category $parent, string $name): string
    {
        $base = Str::slug($name) ?: 'category';
        $slug = $base;

        // Disambiguate common leaves (e.g. "Food", "Toys") by prefixing the parent.
        if ($parent && isset($this->usedSlugs[$slug])) {
            $slug = Str::slug($parent->name_en . '-' . $name);
        }

        $candidate = $slug;
        $n = 2;
        while (isset($this->usedSlugs[$candidate])) {
            $candidate = $slug . '-' . $n;
            $n++;
        }

        $this->usedSlugs[$candidate] = true;
        return $candidate;
    }
}
