<?php
/**
 * MeteoFeed — official lightweight PHP client for the MeteoFeed weather API.
 *
 *   Site:  https://meteofeed.com
 *   Docs:  https://meteofeed.com/docs
 *   Spec:  https://meteofeed.com/openapi/v1.yaml   (generate a full typed client from this if you prefer)
 *
 * Zero dependencies — built-in cURL only. Single file: drop it in (or publish to Packagist).
 * Every endpoint is one bearer-token GET returning JSON; use the named helpers for the common
 * ones or ->get($path, $params) for anything. PHP 8.0+.
 *
 *   require 'meteofeed.php';
 *   use MeteoFeed\Client;
 *
 *   $mf  = new Client('mtfd_your_key');
 *   $now = $mf->current(52.37, 4.89);
 *   echo $now['data']['temperature'], " °C\n";
 *
 *   $gfs    = $mf->forecast(52.37, 4.89, ['hours' => 48, 'model' => 'gfs']);
 *   $quakes = $mf->get('earthquakes', ['min_mag' => 5, 'limit' => 10]);
 *
 * MIT-licensed.
 */

namespace MeteoFeed;

class MeteoFeedException extends \RuntimeException
{
    public int $status;
    public ?string $apiCode;
    public ?int $retryAfter; // seconds, set on 429

    public function __construct(int $status, ?string $apiCode = null, ?string $message = null, ?int $retryAfter = null)
    {
        $this->status = $status;
        $this->apiCode = $apiCode;
        $this->retryAfter = $retryAfter;
        parent::__construct("MeteoFeed API error {$status}" . ($apiCode ? " [{$apiCode}]" : '') . ($message ? ": {$message}" : ''), $status);
    }
}

class Client
{
    private string $baseUrl;

    public function __construct(private string $apiKey, string $baseUrl = 'https://api.meteofeed.com', private int $timeout = 30)
    {
        if ($apiKey === '') {
            throw new \InvalidArgumentException('an API key is required (mtfd_…)');
        }
        $this->baseUrl = rtrim($baseUrl, '/');
    }

    /** GET /v1/<path> with query params (nulls dropped); returns the decoded JSON as an array. */
    public function get(string $path, array $params = []): array
    {
        $params = array_filter($params, fn ($v) => $v !== null);
        $url = $this->baseUrl . '/v1/' . ltrim($path, '/') . ($params ? '?' . http_build_query($params) : '');

        $retryAfter = null;
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => ["Authorization: Bearer {$this->apiKey}", 'Accept: application/json'],
            CURLOPT_TIMEOUT        => $this->timeout,
            CURLOPT_HEADERFUNCTION => function ($ch, $header) use (&$retryAfter) {
                if (stripos($header, 'Retry-After:') === 0) {
                    $retryAfter = (int) trim(substr($header, 12));
                }
                return strlen($header);
            },
        ]);
        $body = curl_exec($ch);
        if ($body === false) {
            $err = curl_error($ch);
            curl_close($ch);
            throw new MeteoFeedException(0, 'transport', $err);
        }
        $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        curl_close($ch);

        $json = json_decode($body, true);
        if ($status >= 400) {
            $e = is_array($json) && isset($json['error']) && is_array($json['error']) ? $json['error'] : [];
            throw new MeteoFeedException($status, $e['code'] ?? null, $e['message'] ?? ($json['message'] ?? null), $retryAfter);
        }
        return is_array($json) ? $json : [];
    }

    // ---- convenience helpers (anything else: ->get($path, $params)) ----
    public function me(): array { return $this->get('me'); }
    public function current($lat, $lng, array $o = []): array { return $this->get('current', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function stations(string $country, array $o = []): array { return $this->get('stations', ['country' => $country] + $o); }
    public function forecast($lat, $lng, array $o = []): array { return $this->get('forecast', ['lat' => $lat, 'lng' => $lng] + $o); } // $o: ['hours'=>, 'model'=>]
    public function forecastShort($lat, $lng, array $o = []): array { return $this->get('forecast/short', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function historical($lat, $lng, string $metric, array $o = []): array { return $this->get('historical', ['lat' => $lat, 'lng' => $lng, 'metric' => $metric] + $o); }
    public function climate($lat, $lng, string $metric, array $o = []): array { return $this->get('climate', ['lat' => $lat, 'lng' => $lng, 'metric' => $metric] + $o); }
    public function warnings(string $region, array $o = []): array { return $this->get('warnings', ['region' => $region] + $o); }
    public function marineForecast($lat, $lng, array $o = []): array { return $this->get('marine/forecast', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function sst($lat, $lng, array $o = []): array { return $this->get('marine/sst', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function airQuality($lat, $lng, array $o = []): array { return $this->get('air-quality', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function pollen($lat, $lng, array $o = []): array { return $this->get('pollen', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function sun($lat, $lng, array $o = []): array { return $this->get('sun', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function uv($lat, $lng, array $o = []): array { return $this->get('uv', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function solar($lat, $lng, array $o = []): array { return $this->get('solar', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function geocode(string $q, array $o = []): array { return $this->get('geocode', ['q' => $q] + $o); }
    public function typhoons(array $o = []): array { return $this->get('typhoons', $o); }
    public function spaceWeather(array $o = []): array { return $this->get('space-weather', $o); }
    public function aurora($lat, $lng, array $o = []): array { return $this->get('aurora', ['lat' => $lat, 'lng' => $lng] + $o); }
    public function earthquakes(array $o = []): array { return $this->get('earthquakes', $o); }
    public function tsunamis(array $o = []): array { return $this->get('tsunamis', $o); }
    public function volcanoes(array $o = []): array { return $this->get('volcanoes', $o); }
    public function avalanche($lat, $lng, array $o = []): array { return $this->get('avalanche', ['lat' => $lat, 'lng' => $lng] + $o); }
}
