<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Support\UsernamePolicy;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class RepairTemporaryUsernamesCommand extends Command
{
    protected $signature = 'skinbase:repair-temp-usernames
        {--chunk=500 : Number of users to process per batch}
        {--dry-run : Preview username changes without writing them}';

    protected $description = 'Replace current users.username values like tmpu% using the users.name field';

    public function handle(): int
    {
        $chunk = max(1, (int) $this->option('chunk'));
        $dryRun = (bool) $this->option('dry-run');

        if ($dryRun) {
            $this->warn('[DRY RUN] No changes will be written.');
        }

        $total = (int) DB::table('users')
            ->where('username', 'like', 'tmpu%')
            ->count();

        if ($total === 0) {
            $this->info('No users with temporary tmpu% usernames were found.');

            return self::SUCCESS;
        }

        $this->info("Found {$total} users with temporary tmpu% usernames.");

        $processed = 0;
        $updated = 0;
        $skipped = 0;

        DB::table('users')
            ->select(['id', 'name', 'username'])
            ->where('username', 'like', 'tmpu%')
            ->chunkById($chunk, function ($rows) use (&$processed, &$updated, &$skipped, $dryRun) {
                foreach ($rows as $row) {
                    $processed++;

                    $sourceName = trim((string) ($row->name ?? ''));
                    if ($sourceName === '') {
                        $skipped++;
                        $this->warn("Skipping user id={$row->id}: name is empty.");
                        continue;
                    }

                    $candidate = $this->resolveCandidate($sourceName, (int) $row->id);

                    if ($candidate === null || strcasecmp($candidate, (string) $row->username) === 0) {
                        $skipped++;
                        $this->warn("Skipping user id={$row->id}: unable to resolve a better username from name='{$sourceName}'.");
                        continue;
                    }

                    if ($dryRun) {
                        $this->line("[dry] Would update user id={$row->id} username '{$row->username}' => '{$candidate}'");
                        $updated++;
                        continue;
                    }

                    $affected = DB::table('users')
                        ->where('id', (int) $row->id)
                        ->where('username', 'like', 'tmpu%')
                        ->update([
                            'username' => $candidate,
                            'username_changed_at' => now(),
                            'updated_at' => now(),
                        ]);

                    if ($affected > 0) {
                        $updated += $affected;
                        $this->line("[update] user id={$row->id} username '{$row->username}' => '{$candidate}'");
                    }
                }
            }, 'id');

        $this->info(sprintf('Finished. processed=%d updated=%d skipped=%d', $processed, $updated, $skipped));

        return self::SUCCESS;
    }

    private function resolveCandidate(string $sourceName, int $userId): ?string
    {
        $base = UsernamePolicy::sanitizeLegacy($sourceName);
        $min = UsernamePolicy::min();
        $max = UsernamePolicy::max();

        if ($base === '') {
            return null;
        }

        if (preg_match('/^tmpu\d+$/i', $base) === 1) {
            $base = 'user' . $userId;
        }

        if (strlen($base) < $min) {
            $base = substr($base . $userId, 0, $max);
        }

        if ($base === '' || $base === 'user') {
            $base = 'user' . $userId;
        }

        $candidate = substr($base, 0, $max);
        $suffix = 1;

        while ($this->usernameExists($candidate, $userId) || UsernamePolicy::isReserved($candidate)) {
            $suffixValue = (string) $suffix;
            $prefixLen = max(1, $max - strlen($suffixValue));
            $candidate = substr($base, 0, $prefixLen) . $suffixValue;
            $suffix++;
        }

        return $candidate;
    }

    private function usernameExists(string $username, int $ignoreUserId): bool
    {
        return DB::table('users')
            ->whereRaw('LOWER(username) = ?', [strtolower($username)])
            ->where('id', '!=', $ignoreUserId)
            ->exists();
    }
}
