<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\User;
use App\Support\UsernamePolicy;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class EnforceUsernamePolicy extends Command
{
    protected $signature = 'skinbase:enforce-usernames {--dry-run : Report only, no writes}';

    protected $description = 'Normalize and enforce username policy on existing users, with collision resolution and redirect logging.';

    public function handle(): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $logPath = storage_path('logs/username_migration.log');
        @file_put_contents($logPath, '['.now()."] enforce-usernames dry_run=".($dryRun ? '1' : '0')."\n", FILE_APPEND);

        $used = User::query()->whereNotNull('username')->pluck('id', 'username')->mapWithKeys(fn ($id, $username) => [strtolower((string) $username) => (int) $id])->all();

        $updated = 0;

        User::query()->orderBy('id')->chunkById(500, function ($users) use (&$used, &$updated, $dryRun, $logPath): void {
            foreach ($users as $user) {
                $current = strtolower(trim((string) ($user->username ?? '')));
                $base = UsernamePolicy::sanitizeLegacy($current !== '' ? $current : ('user'.$user->id));

                if (UsernamePolicy::isReserved($base) || UsernamePolicy::similarReserved($base) !== null) {
                    $base = 'user'.$user->id;
                }

                $candidate = substr($base, 0, UsernamePolicy::max());
                $suffix = 1;
                while ((isset($used[$candidate]) && (int) $used[$candidate] !== (int) $user->id) || UsernamePolicy::isReserved($candidate) || UsernamePolicy::similarReserved($candidate) !== null) {
                    $suffixStr = (string) $suffix;
                    $prefixLen = max(1, UsernamePolicy::max() - strlen($suffixStr));
                    $candidate = substr($base, 0, $prefixLen) . $suffixStr;
                    $suffix++;
                }

                $needsUpdate = $candidate !== $current;
                if (! $needsUpdate) {
                    $used[$candidate] = (int) $user->id;
                    continue;
                }

                @file_put_contents($logPath, sprintf("[%s] user_id=%d old=%s new=%s\n", now()->toDateTimeString(), (int) $user->id, $current, $candidate), FILE_APPEND);

                if (! $dryRun) {
                    DB::transaction(function () use ($user, $current, $candidate): void {
                        if ($current !== '' && Schema::hasTable('username_history')) {
                            DB::table('username_history')->insert([
                                'user_id' => (int) $user->id,
                                'old_username' => $current,
                                'changed_at' => now(),
                                'created_at' => now(),
                                'updated_at' => now(),
                            ]);
                        }

                        if ($current !== '' && Schema::hasTable('username_redirects')) {
                            DB::table('username_redirects')->updateOrInsert(
                                ['old_username' => $current],
                                [
                                    'new_username' => $candidate,
                                    'user_id' => (int) $user->id,
                                    'created_at' => now(),
                                    'updated_at' => now(),
                                ]
                            );
                        }

                        DB::table('users')->where('id', (int) $user->id)->update([
                            'username' => $candidate,
                            'username_changed_at' => now(),
                            'updated_at' => now(),
                        ]);
                    });
                }

                $used[$candidate] = (int) $user->id;
                $updated++;
            }
        });

        $this->info("Username policy enforcement complete. Updated: {$updated}" . ($dryRun ? ' (dry run)' : ''));

        return self::SUCCESS;
    }
}
