<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

return new class extends Migration
{
    public function up(): void
    {
        $this->ensureCollectionSlugs();
        $this->ensureTaskSlugs();
    }

    public function down(): void
    {
        $this->rollbackCollectionSlugs();
        $this->rollbackTaskSlugs();
    }

    protected function ensureCollectionSlugs(): void
    {
        if (! Schema::hasTable('task_collections') || Schema::hasColumn('task_collections', 'slug')) {
            return;
        }

        Schema::table('task_collections', function (Blueprint $table) {
            $table->string('slug')->nullable()->after('tenant_id');
        });

        DB::table('task_collections')
            ->select('id', 'slug', 'name_translations')
            ->orderBy('id')
            ->chunk(200, function ($rows) {
                foreach ($rows as $row) {
                    if (! empty($row->slug)) {
                        continue;
                    }

                    $translations = $this->decodeTranslations($row->name_translations);
                    $base = $translations['en'] ?? $translations['de'] ?? reset($translations) ?? ('collection-' . $row->id);
                    $slug = $this->buildUniqueSlug($base, 'collection-', function ($candidate) {
                        return DB::table('task_collections')->where('slug', $candidate)->exists();
                    });

                    DB::table('task_collections')
                        ->where('id', $row->id)
                        ->update(['slug' => $slug]);
                }
            });

        Schema::table('task_collections', function (Blueprint $table) {
            $table->unique('slug');
        });
    }

    protected function ensureTaskSlugs(): void
    {
        if (! Schema::hasTable('tasks') || Schema::hasColumn('tasks', 'slug')) {
            return;
        }

        Schema::table('tasks', function (Blueprint $table) {
            $table->string('slug')->nullable()->after('id');
        });

        DB::table('tasks')
            ->select('id', 'slug', 'title')
            ->orderBy('id')
            ->chunk(200, function ($rows) {
                foreach ($rows as $row) {
                    if (! empty($row->slug)) {
                        continue;
                    }

                    $translations = $this->decodeTranslations($row->title);
                    $base = $translations['en'] ?? $translations['de'] ?? reset($translations) ?? ('task-' . $row->id);
                    $slug = $this->buildUniqueSlug($base, 'task-', function ($candidate) {
                        return DB::table('tasks')->where('slug', $candidate)->exists();
                    });

                    DB::table('tasks')
                        ->where('id', $row->id)
                        ->update(['slug' => $slug]);
                }
            });

        Schema::table('tasks', function (Blueprint $table) {
            $table->unique('slug');
        });
    }

    protected function rollbackCollectionSlugs(): void
    {
        if (! Schema::hasTable('task_collections') || ! Schema::hasColumn('task_collections', 'slug')) {
            return;
        }

        Schema::table('task_collections', function (Blueprint $table) {
            $table->dropUnique('task_collections_slug_unique');
            $table->dropColumn('slug');
        });
    }

    protected function rollbackTaskSlugs(): void
    {
        if (! Schema::hasTable('tasks') || ! Schema::hasColumn('tasks', 'slug')) {
            return;
        }

        Schema::table('tasks', function (Blueprint $table) {
            $table->dropUnique('tasks_slug_unique');
            $table->dropColumn('slug');
        });
    }

    /**
     * @return array<string, string>
     */
    protected function decodeTranslations(mixed $value): array
    {
        if (is_array($value)) {
            return $value;
        }

        if (is_string($value)) {
            $decoded = json_decode($value, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                return $decoded;
            }

            return ['de' => $value];
        }

        return [];
    }

    protected function buildUniqueSlug(string $base, string $prefix, callable $exists): string
    {
        $slugBase = Str::slug($base) ?: ($prefix . Str::random(4));

        do {
            $candidate = $slugBase . '-' . Str::random(4);
        } while ($exists($candidate));

        return $candidate;
    }
};
