<?php

namespace App\Services;

use App\Models\HealthCheck;
use Illuminate\Support\Facades\Http;
use Throwable;

/**
 * Runs an uptime probe NATIVELY (Laravel HTTP client / a PHP TCP stream) — no SSH, no shell, so the
 * target is never interpolated into a command. An HTTP probe reports only up/down + status code +
 * latency (never the response body), so it can't exfiltrate an internal endpoint's content. Redirects
 * are disabled so a 30x can't bounce the probe to an unseen target.
 */
class HealthService
{
    /**
     * @return array{ok:bool, latency:?int, detail:string}
     */
    public function probe(HealthCheck $check, int $timeout = 5): array
    {
        return $check->type === 'tcp'
            ? $this->tcp((string) $check->target, (int) $check->port, $timeout)
            : $this->http((string) $check->target, $timeout);
    }

    private function http(string $url, int $timeout): array
    {
        $start = microtime(true);
        try {
            $res = Http::timeout($timeout)->withoutRedirecting()->get($url);
            $latency = (int) round((microtime(true) - $start) * 1000);
            $status = $res->status();

            // 2xx/3xx = up; a 4xx/5xx means the endpoint answered but is unhealthy.
            return ['ok' => $status >= 200 && $status < 400, 'latency' => $latency, 'detail' => 'HTTP '.$status];
        } catch (Throwable $e) {
            return ['ok' => false, 'latency' => null, 'detail' => $this->reason($e->getMessage())];
        }
    }

    private function tcp(string $host, int $port, int $timeout): array
    {
        $start = microtime(true);
        $errno = 0;
        $errstr = '';
        $sock = @stream_socket_client('tcp://'.$host.':'.$port, $errno, $errstr, $timeout);

        if (! $sock) {
            return ['ok' => false, 'latency' => null, 'detail' => $errstr !== '' ? $errstr : 'connection failed'];
        }

        fclose($sock);
        $latency = (int) round((microtime(true) - $start) * 1000);

        return ['ok' => true, 'latency' => $latency, 'detail' => 'TCP '.$port.' open'];
    }

    /** Keep the failure reason short + on one line for the compact status row. */
    private function reason(string $msg): string
    {
        $msg = trim(preg_split('/\R/', $msg)[0] ?? $msg);

        return mb_strlen($msg) > 80 ? mb_substr($msg, 0, 80).'…' : $msg;
    }
}
