<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Events\ProvisioningFailed;
use App\Models\ProvisioningLog;
use App\Models\Service;
use App\Services\Provisioning\ProvisioningFactory;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class RetryProvisioningCommand extends Command
{
    protected $signature = 'provisioning:retry
        {--max-attempts=3 : Maximum number of retry attempts per service}';

    protected $description = 'Retry failed provisioning attempts';

    public function handle(ProvisioningFactory $provisioningFactory): int
    {
        $maxAttempts = (int) $this->option('max-attempts');

        $failedServices = Service::query()
            ->where('status', 'failed')
            ->where('created_at', '>=', now()->subDays(7))
            ->with(['subscription', 'plan'])
            ->get();

        if ($failedServices->isEmpty()) {
            $this->info('No failed services found to retry.');

            return self::SUCCESS;
        }

        $this->info("Found {$failedServices->count()} failed service(s) to evaluate.");

        $retried = 0;
        $skipped = 0;
        $succeeded = 0;
        $failed = 0;

        foreach ($failedServices as $service) {
            $failedAttempts = ProvisioningLog::query()
                ->where('service_id', $service->id)
                ->where('action', 'provision')
                ->where('status', 'failed')
                ->count();

            if ($failedAttempts >= $maxAttempts) {
                $this->warn("Service #{$service->id}: skipped (already at {$failedAttempts}/{$maxAttempts} attempts).");

                if ($failedAttempts === $maxAttempts) {
                    ProvisioningFailed::dispatch(
                        $service,
                        'Maximum retry attempts reached',
                        $failedAttempts,
                        true,
                    );
                }

                $skipped++;

                continue;
            }

            if (! $service->subscription) {
                $this->warn("Service #{$service->id}: skipped (no associated subscription).");
                $skipped++;

                continue;
            }

            $attemptNumber = $failedAttempts + 1;
            $this->info("Service #{$service->id}: retrying provisioning (attempt {$attemptNumber}/{$maxAttempts})...");

            try {
                $provisioningService = $provisioningFactory->make($service->service_type);

                // provision() is idempotent — it reuses the existing Service record
                $retryService = $provisioningService->provision($service->subscription);

                $this->info("Service #{$retryService->id}: provisioned successfully.");
                Log::info("Provisioning retry succeeded for service #{$retryService->id}", [
                    'service_id' => $retryService->id,
                    'service_type' => $service->service_type,
                    'attempt' => $attemptNumber,
                ]);

                $retried++;
                $succeeded++;
            } catch (\Throwable $e) {
                $this->error("Service #{$service->id}: provisioning failed — {$e->getMessage()}");
                Log::error("Provisioning retry failed for service #{$service->id}", [
                    'service_id' => $service->id,
                    'service_type' => $service->service_type,
                    'attempt' => $attemptNumber,
                    'error' => $e->getMessage(),
                ]);

                $isMaxReached = $attemptNumber >= $maxAttempts;

                ProvisioningFailed::dispatch(
                    $service->fresh() ?? $service,
                    $e->getMessage(),
                    $attemptNumber,
                    $isMaxReached,
                );

                $retried++;
                $failed++;
            }
        }

        $this->newLine();
        $this->info("Retry summary: {$retried} retried, {$succeeded} succeeded, {$failed} failed, {$skipped} skipped.");

        return self::SUCCESS;
    }
}
