<?php

namespace App\Services\Monitoring;

use App\Services\Secrets\SecretVault;
use App\Support\ProvisioningSettings;
use Illuminate\Support\Facades\Http;

/**
 * Registers uptime targets with the configured monitoring service. If none is
 * configured, returns a deterministic id (no-op) so provisioning still records a
 * stable breadcrumb. Not unit-tested (live I/O).
 *
 * The bridge URL comes from ProvisioningSettings (console-stored, falling
 * back to MONITORING_API_URL); the bridge token comes from the vault
 * (console-stored, falling back to MONITORING_API_TOKEN) — both read here, at
 * the point of use, never overlaid onto config at boot. See SecretVault's
 * docblock (rule 3).
 */
class HttpMonitoringClient implements MonitoringClient
{
    public function registerTarget(string $name, string $url): string
    {
        $endpoint = ProvisioningSettings::monitoringUrl();
        if (blank($endpoint)) {
            return 'mon-'.substr(sha1($url), 0, 12);
        }

        $base = rtrim($endpoint, '/');
        $token = (string) app(SecretVault::class)->get('monitoring.token');

        // Idempotent: reuse an existing monitor for this URL rather than creating
        // a duplicate when a crash retries after a prior successful POST.
        $existing = collect(Http::withToken($token)->get($base.'/monitors')->throw()->json('monitors', []))
            ->first(fn ($monitor) => ($monitor['url'] ?? null) === $url);
        if ($existing !== null) {
            return (string) $existing['id'];
        }

        return (string) Http::withToken($token)
            ->post($base.'/monitors', ['friendly_name' => $name, 'url' => $url, 'type' => 'https'])
            ->throw()->json('monitor.id');
    }

    public function isHealthy(string $externalId): bool
    {
        // Unknown counts as healthy HERE, and only here: this is the
        // provisioning acceptance check, which must not fail a delivery because
        // monitoring is not set up.
        return $this->health($externalId) ?? true;
    }

    public function health(string $externalId): ?bool
    {
        $endpoint = ProvisioningSettings::monitoringUrl();

        if (blank($endpoint)) {
            return null; // nothing is watching, which is not the same as healthy
        }

        try {
            $monitor = Http::withToken((string) app(SecretVault::class)->get('monitoring.token'))
                ->get(rtrim($endpoint, '/').'/monitors/'.$externalId)->throw()->json('monitor', []);
        } catch (\Throwable) {
            return null; // the monitor is unreachable, which says nothing about the instance
        }

        return ($monitor['status'] ?? '') === 'up';
    }

    public function deregisterTarget(string $externalId): void
    {
        $endpoint = ProvisioningSettings::monitoringUrl();
        if (filled($endpoint)) {
            Http::withToken((string) app(SecretVault::class)->get('monitoring.token'))
                ->delete(rtrim($endpoint, '/').'/monitors/'.$externalId)->throw();
        }
    }
}
