<?php

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

return new class extends Migration
{
    public function up(): void
    {
        $locations = DB::table('tenants')
            ->select(['id', 'dominio'])
            ->orderBy('id')
            ->get()
            ->mapWithKeys(function (object $tenant): array {
                [$domain, $basePath] = $this->splitTenantLocation($tenant->dominio, $tenant->id);

                return [$tenant->id => compact('domain', 'basePath')];
            });
        $duplicates = $locations
            ->groupBy(fn (array $location): string => $location['domain'].'|'.$location['basePath'])
            ->filter(fn ($matches): bool => $matches->count() > 1);

        if ($duplicates->isNotEmpty()) {
            throw new RuntimeException(
                'Cannot create the tenant domain/base-path unique index; duplicates exist: '
                .$duplicates->keys()->implode(', ')
            );
        }

        Schema::table('tenants', function (Blueprint $table) {
            $table->string('base_path')->default('/')->after('dominio');
            $table->dropUnique('tenants_dominio_unique');
        });

        foreach ($locations as $tenantId => $location) {
            DB::table('tenants')
                ->where('id', $tenantId)
                ->update([
                    'dominio' => $location['domain'],
                    'base_path' => $location['basePath'],
                ]);
        }

        Schema::table('tenants', function (Blueprint $table) {
            $table->unique(
                ['dominio', 'base_path'],
                'tenants_dominio_base_path_unique',
            );
        });
    }

    public function down(): void
    {
        Schema::table('tenants', function (Blueprint $table) {
            $table->dropUnique('tenants_dominio_base_path_unique');
        });

        DB::table('tenants')
            ->select(['id', 'dominio', 'base_path'])
            ->orderBy('id')
            ->each(function (object $tenant): void {
                $tenantKey = $tenant->dominio.($tenant->base_path === '/' ? '' : $tenant->base_path);

                DB::table('tenants')
                    ->where('id', $tenant->id)
                    ->update(['dominio' => $tenantKey]);
            });

        Schema::table('tenants', function (Blueprint $table) {
            $table->dropColumn('base_path');
            $table->unique('dominio', 'tenants_dominio_unique');
        });
    }

    /** @return array{string, string} */
    private function splitTenantLocation(mixed $value, int $tenantId): array
    {
        if (! is_string($value) || trim(urldecode($value)) === '') {
            throw new RuntimeException("Cannot migrate tenant location for tenant {$tenantId}.");
        }

        $decodedValue = trim(urldecode($value));
        $candidate = str_contains($decodedValue, '://')
            ? $decodedValue
            : "//{$decodedValue}";
        $host = parse_url($candidate, PHP_URL_HOST);
        $path = parse_url($candidate, PHP_URL_PATH) ?: '/';

        if (! is_string($host) || $host === '') {
            throw new RuntimeException("Cannot migrate tenant location '{$value}' for tenant {$tenantId}.");
        }

        $segments = array_values(array_filter(
            explode('/', preg_replace('#/+#', '/', $path) ?? ''),
            static fn (string $segment): bool => $segment !== '',
        ));

        if (array_intersect($segments, ['.', '..']) !== []) {
            throw new RuntimeException("Cannot migrate tenant base path '{$path}' for tenant {$tenantId}.");
        }

        return [
            strtolower($host),
            $segments === [] ? '/' : '/'.implode('/', $segments),
        ];
    }
};
