<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Jobs\RecomputeUserStatsJob;
use App\Services\UserStatsService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

/**
 * Recompute user_statistics counters from authoritative source tables.
 *
 * Usage:
 *   # Recompute a single user (live)
 *   php artisan skinbase:recompute-user-stats 42
 *
 *   # Dry-run for a single user
 *   php artisan skinbase:recompute-user-stats 42 --dry-run
 *
 *   # Recompute all users in chunks of 500
 *   php artisan skinbase:recompute-user-stats --all --chunk=500
 *
 *   # Recompute all users via queue (one job per chunk)
 *   php artisan skinbase:recompute-user-stats --all --queue
 */
class RecomputeUserStatsCommand extends Command
{
    protected $signature = 'skinbase:recompute-user-stats
                            {user_id? : The ID of a single user to recompute}
                            {--all    : Recompute stats for ALL non-deleted users}
                            {--chunk=1000 : Chunk size when --all is used}
                            {--dry-run    : Show what would be written without saving}
                            {--queue      : Dispatch recompute jobs to the queue (--all mode only)}';

    protected $description = 'Rebuild user_statistics counters from authoritative source tables';

    public function handle(UserStatsService $statsService): int
    {
        $dryRun  = (bool) $this->option('dry-run');
        $all     = (bool) $this->option('all');
        $userId  = $this->argument('user_id');
        $chunk   = max(1, (int) $this->option('chunk'));
        $queue   = (bool) $this->option('queue');

        if ($userId !== null && $all) {
            $this->error('Provide either a user_id OR --all, not both.');
            return self::FAILURE;
        }

        if ($userId !== null) {
            return $this->recomputeSingle((int) $userId, $statsService, $dryRun);
        }

        if ($all) {
            return $this->recomputeAll($statsService, $chunk, $dryRun, $queue);
        }

        $this->error('Provide a user_id or use --all.');
        return self::FAILURE;
    }

    // ─── Single user ─────────────────────────────────────────────────────────

    private function recomputeSingle(int $userId, UserStatsService $statsService, bool $dryRun): int
    {
        $exists = DB::table('users')->where('id', $userId)->exists();
        if (! $exists) {
            $this->error("User {$userId} not found.");
            return self::FAILURE;
        }

        $label = $dryRun ? '[DRY-RUN]' : '[LIVE]';
        $this->line("{$label} Recomputing stats for user #{$userId}…");

        $computed = $statsService->recomputeUser($userId, $dryRun);

        $rows = [];
        foreach ($computed as $col => $val) {
            $rows[] = [$col, $val ?? '(null)'];
        }

        $this->table(['Column', 'Value'], $rows);

        if ($dryRun) {
            $this->warn('Dry-run: no changes written.');
        } else {
            $this->info("Stats saved for user #{$userId}.");
        }

        return self::SUCCESS;
    }

    // ─── All users ────────────────────────────────────────────────────────────

    private function recomputeAll(
        UserStatsService $statsService,
        int $chunk,
        bool $dryRun,
        bool $useQueue
    ): int {
        $total = DB::table('users')->whereNull('deleted_at')->count();
        $label = $dryRun ? '[DRY-RUN]' : ($useQueue ? '[QUEUE]' : '[LIVE]');

        $this->info("{$label} Recomputing stats for {$total} users (chunk={$chunk})…");

        if ($useQueue && ! $dryRun) {
            $dispatched = 0;
            DB::table('users')
                ->whereNull('deleted_at')
                ->orderBy('id')
                ->chunkById($chunk, function ($users) use (&$dispatched) {
                    $ids = $users->pluck('id')->all();
                    RecomputeUserStatsJob::dispatch($ids);
                    $dispatched += count($ids);
                    $this->line("  Queued chunk of " . count($ids) . " users (total dispatched: {$dispatched})");
                });

            $this->info("Done – {$dispatched} users queued for recompute.");
            return self::SUCCESS;
        }

        $processed = 0;
        $bar = $this->output->createProgressBar($total);
        $bar->start();

        DB::table('users')
            ->whereNull('deleted_at')
            ->orderBy('id')
            ->chunkById($chunk, function ($users) use ($statsService, $dryRun, &$processed, $bar) {
                foreach ($users as $user) {
                    $statsService->recomputeUser((int) $user->id, $dryRun);
                    $processed++;
                    $bar->advance();
                }
            });

        $bar->finish();
        $this->newLine();

        $suffix = $dryRun ? ' (no changes written – dry-run)' : '';
        $this->info("Done – {$processed} users recomputed{$suffix}.");

        return self::SUCCESS;
    }
}
