<?php

namespace App\Provisioning\Steps\Customer;

use App\Models\ProvisioningRun;
use App\Provisioning\StepResult;
use App\Services\Monitoring\MonitoringClient;
use App\Support\ProvisioningSettings;

class RegisterMonitoring extends CustomerStep
{
    public function __construct(private MonitoringClient $monitoring) {}

    public function key(): string
    {
        return 'register_monitoring';
    }

    public function maxDuration(): int
    {
        return 120;
    }

    public function execute(ProvisioningRun $run): StepResult
    {
        if ($this->hasResource($run, 'monitoring_target_id')) {
            return StepResult::advance();
        }

        $instance = $this->instance($run);
        $url = 'https://'.$instance->subdomain.'.'.ProvisioningSettings::dnsZone().'/status.php';

        // Register with the monitoring service [E] before recording it.
        // Monitoring is observability, not the product: a paid customer's cloud
        // must not fail to be delivered because Kuma/the bridge is down. Retry a
        // few times, then continue DEGRADED with a visible event — unless the
        // operator declares monitoring mandatory.
        try {
            $targetId = $this->monitoring->registerTarget($instance->subdomain, $url);
        } catch (\Throwable $e) {
            if (config('provisioning.monitoring.required', false)) {
                throw $e; // operator wants monitoring to gate delivery
            }

            // `attempt` is 0-based (this call IS attempt number attempt+1), so
            // compare one-based: MONITORING_ATTEMPTS=2 really means two tries.
            // Cap at the run's own retry budget — otherwise a large ATTEMPTS
            // value would burn through max_attempts and fail the run, which is
            // exactly what this degradation exists to prevent.
            $configured = max(1, (int) config('provisioning.monitoring.attempts', 2));
            $allowed = max(1, min($configured, (int) $run->max_attempts));
            if ($run->attempt + 1 < $allowed) {
                return StepResult::retry(30, 'monitoring unavailable: '.$e->getMessage());
            }

            // Give up on monitoring, keep the cloud. Recorded so it is visible in
            // the admin console rather than silently missing.
            $run->events()->create([
                'step' => $this->key(),
                'attempt' => $run->attempt,
                'outcome' => 'info',
                'message' => 'Monitoring übersprungen (Dienst nicht erreichbar): '.$e->getMessage(),
            ]);

            return StepResult::advance();
        }

        // Local row BEFORE the breadcrumb (the short-circuit guard).
        $instance->monitoringTargets()->firstOrCreate(
            ['external_id' => $targetId],
            // 'unknown', not 'up': registering a check is not the same as
            // passing one, and the sync job is what turns it into a verdict.
            ['url' => $url, 'status' => 'unknown'],
        );
        $this->recordResource($run, $instance->host, 'monitoring_target_id', $targetId);

        return StepResult::advance();
    }
}
