<?php

namespace App\Console\Commands;

use App\Models\Branch;
use App\Models\Customer;
use App\Services\BranchContext;
use App\Services\CustomerRiskService;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Throwable;

class RefreshStaleCreditScores extends Command
{
    protected $signature = 'credit-scores:refresh-stale
        {--hours= : Refresh profiles older than this many hours}
        {--chunk= : Number of customers to process per database chunk}
        {--branch= : Limit the refresh to one branch ID}';

    protected $description = 'Refresh missing or stale current customer credit scores without creating history snapshots';

    public function handle(BranchContext $branchContext, CustomerRiskService $risk): int
    {
        $hours = $this->positiveIntOption('hours', (int) config('credit_score.operations.stale_after_hours', 24));
        $chunkSize = $this->positiveIntOption('chunk', (int) config('credit_score.operations.refresh_chunk_size', 100));
        $branchOption = $this->option('branch');
        $branchId = filled($branchOption) ? (int) $branchOption : null;

        if ($branchId !== null && $branchId < 1) {
            $this->error('The --branch option must be a positive branch ID.');

            return self::FAILURE;
        }

        $branchIds = Branch::query()
            ->when($branchId !== null, fn ($query) => $query->whereKey($branchId))
            ->orderBy('id')
            ->pluck('id')
            ->map(fn ($id) => (int) $id)
            ->all();

        if ($branchId !== null && $branchIds === []) {
            $this->error("Branch {$branchId} was not found.");

            return self::FAILURE;
        }

        $cutoff = CarbonImmutable::now()->subHours($hours);
        $scoreVersion = (string) config('credit_score.version', 'v1');
        $refreshed = 0;
        $failures = 0;

        foreach ($branchIds as $currentBranchId) {
            $branchContext->forBranch($currentBranchId, function () use (
                $risk,
                $cutoff,
                $scoreVersion,
                $chunkSize,
                &$refreshed,
                &$failures,
            ): void {
                Customer::query()
                    ->where(function ($query) use ($cutoff, $scoreVersion): void {
                        $query->whereDoesntHave('riskProfile')
                            ->orWhereHas('riskProfile', function ($riskQuery) use ($cutoff, $scoreVersion): void {
                                $riskQuery->whereNull('calculated_at')
                                    ->orWhere('calculated_at', '<', $cutoff)
                                    ->orWhereNull('score_version')
                                    ->orWhere('score_version', '!=', $scoreVersion);
                            });
                    })
                    ->orderBy('id')
                    ->chunkById($chunkSize, function ($customers) use ($risk, &$refreshed, &$failures): void {
                        foreach ($customers as $customer) {
                            try {
                                $risk->refresh($customer, 'stale_daily_refresh');
                                $refreshed++;
                            } catch (Throwable $exception) {
                                $failures++;
                                report($exception);
                                $this->warn("Credit score refresh failed for customer {$customer->getKey()}.");
                            }
                        }
                    });
            });
        }

        $this->info(sprintf(
            'Credit score stale refresh complete: %d refreshed, %d failed across %d branch(es).',
            $refreshed,
            $failures,
            count($branchIds),
        ));

        return $failures > 0 ? self::FAILURE : self::SUCCESS;
    }

    private function positiveIntOption(string $name, int $default): int
    {
        $value = $this->option($name);
        if (blank($value)) {
            return max(1, $default);
        }

        return max(1, (int) $value);
    }
}
