<?php

namespace App\Provisioning\Steps\Customer;

use App\Models\ProvisioningRun;
use App\Provisioning\StepResult;
use App\Services\Dns\HetznerDnsClient;
use App\Services\Traefik\TraefikWriter;
use App\Support\ProvisioningSettings;

class ConfigureDnsAndTls extends CustomerStep
{
    public function __construct(
        private HetznerDnsClient $dns,
        private TraefikWriter $traefik,
    ) {}

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

    public function maxDuration(): int
    {
        // Above the 840s cert own-deadline below, so the step's own fail fires first.
        return 960;
    }

    public function execute(ProvisioningRun $run): StepResult
    {
        $instance = $this->instance($run);
        $host = $instance->host;
        $fqdn = $instance->subdomain.'.'.ProvisioningSettings::dnsZone();

        // DNS — provider upsert is idempotent; the local row uses firstOrCreate on
        // the record id and is written BEFORE the breadcrumb (the short-circuit guard).
        if (! $this->hasResource($run, 'dns_record_id')) {
            $recordId = $this->dns->upsertRecord($fqdn, 'A', $host->public_ip);
            $instance->dnsRecords()->firstOrCreate(
                ['record_id' => $recordId],
                ['provider' => 'hetzner', 'fqdn' => $fqdn, 'type' => 'A', 'value' => $host->public_ip],
            );
            $this->recordResource($run, $host, 'dns_record_id', $recordId);
        }

        // Traefik file-provider route, written on the serving host (DNS points at
        // it) and pointing at the guest VM.
        if (! $instance->route_written) {
            $trafficHost = $host->wg_ip ?? $host->public_ip;
            $backend = $instance->guest_ip ?: $host->public_ip;
            $this->traefik->write($trafficHost, $instance->subdomain, $backend);
            $instance->update(['route_written' => true]);
        }

        // TLS via HTTP-01 — poll until the certificate is served.
        if (! $instance->cert_ok) {
            if ($this->traefik->certReachable($fqdn)) {
                $instance->update(['cert_ok' => true]);
            } elseif ($run->started_at !== null && $run->started_at->copy()->addSeconds(840)->isPast()) {
                return StepResult::fail('cert_timeout');
            } else {
                return StepResult::poll(20, 'waiting for certificate');
            }
        }

        return StepResult::advance();
    }
}
