<?php
/**
 * AriyaTips Global Alert API v2
 * PHP 8.x compatible.
 *
 * Public health check:
 *   GET /ariya-alerts-api.php
 *   GET /ariya-alerts-api.php?action=health
 *
 * Protected operations:
 *   GET  ?action=list&key=...&minutes=5&limit=5
 *   POST action=push with key, time, text
 */

declare(strict_types=1);

// IMPORTANT: replace this with the SAME long random secret used in AriyaTips.
const API_KEY = 'CHANGE_THIS_TO_A_LONG_RANDOM_SECRET';
const DATA_FILE = __DIR__ . '/ariyatips-alerts.json';
const MAX_ALERTS = 100;
const MAX_AGE_SECONDS = 3600; // Keep server-side history for up to 1 hour.

header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

function fail_response(int $code, string $message): never
{
    http_response_code($code);
    echo $message . "\n";
    exit;
}

$action = strtolower(trim((string)($_REQUEST['action'] ?? '')));

// A simple public health check makes browser testing easy.
// No secret is exposed and no alert data is returned.
if ($action === '' || $action === 'health') {
    echo "ARIYATIPS API OK\n";
    echo "PHP " . PHP_VERSION . "\n";
    echo "HTTPS " . ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'ON' : 'OFF') . "\n";
    exit;
}

$key = (string)($_REQUEST['key'] ?? '');
if (API_KEY === 'CHANGE_THIS_TO_A_LONG_RANDOM_SECRET') {
    fail_response(500, 'API_KEY_NOT_CONFIGURED');
}

if ($key === '' || !hash_equals(API_KEY, $key)) {
    fail_response(403, 'FORBIDDEN');
}

if ($action !== 'push' && $action !== 'list') {
    fail_response(400, 'INVALID_ACTION');
}

$now = time();

// Create the data file if it does not exist. The directory must be writable by PHP.
$fp = @fopen(DATA_FILE, 'c+');
if ($fp === false) {
    fail_response(500, 'STORAGE_ERROR');
}

if (!flock($fp, LOCK_EX)) {
    fclose($fp);
    fail_response(500, 'LOCK_ERROR');
}

$contents = stream_get_contents($fp);
$alerts = [];

if ($contents !== false && trim($contents) !== '') {
    $decoded = json_decode($contents, true);
    if (is_array($decoded)) {
        $alerts = $decoded;
    }
}

// Remove malformed, expired and future records.
$filtered = [];
foreach ($alerts as $a) {
    if (!is_array($a) || !isset($a['time'], $a['text'])) {
        continue;
    }

    $ts = strtotime((string)$a['time']);
    if ($ts === false || ($now - $ts) < 0 || ($now - $ts) > MAX_AGE_SECONDS) {
        continue;
    }

    $filtered[] = [
        'time' => gmdate('c', $ts),
        'text' => substr((string)$a['text'], 0, 500),
    ];
}

if ($action === 'push') {
    if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
        flock($fp, LOCK_UN);
        fclose($fp);
        fail_response(405, 'POST_REQUIRED');
    }

    $time = (string)($_POST['time'] ?? '');
    $text = trim((string)($_POST['text'] ?? ''));
    $ts = strtotime($time);

    if ($ts === false || $text === '') {
        flock($fp, LOCK_UN);
        fclose($fp);
        fail_response(400, 'INVALID_ALERT');
    }

    // Allow up to 5 minutes of future clock difference and 1 hour of stale data.
    if (($now - $ts) > MAX_AGE_SECONDS || ($ts - $now) > 300) {
        flock($fp, LOCK_UN);
        fclose($fp);
        fail_response(400, 'ALERT_TIME_OUT_OF_RANGE');
    }

    $newAlert = [
        'time' => gmdate('c', $ts),
        'text' => substr(str_replace(["\r", "\n", '|'], [' ', ' ', '/'], $text), 0, 500),
    ];

    $duplicate = false;
    foreach ($filtered as $a) {
        if ($a['time'] === $newAlert['time'] && $a['text'] === $newAlert['text']) {
            $duplicate = true;
            break;
        }
    }

    if (!$duplicate) {
        array_unshift($filtered, $newAlert);
    }
}

usort($filtered, static function (array $a, array $b): int {
    return strcmp($b['time'], $a['time']);
});
$filtered = array_slice($filtered, 0, MAX_ALERTS);

// Rewrite the compact JSON store while holding the lock.
rewind($fp);
ftruncate($fp, 0);
$json = json_encode($filtered, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if ($json === false || fwrite($fp, $json) === false) {
    flock($fp, LOCK_UN);
    fclose($fp);
    fail_response(500, 'WRITE_ERROR');
}
fflush($fp);

if ($action === 'push') {
    flock($fp, LOCK_UN);
    fclose($fp);
    echo "OK\n";
    exit;
}

$minutes = max(1, min(60, (int)($_GET['minutes'] ?? 5)));
$limit = max(1, min(20, (int)($_GET['limit'] ?? 5)));
$cutoff = $now - ($minutes * 60);

$count = 0;
foreach ($filtered as $a) {
    $ts = strtotime($a['time']);
    if ($ts === false || $ts < $cutoff || $ts > $now + 300) {
        continue;
    }

    echo $a['time'] . '|' . $a['text'] . "\n";
    $count++;
    if ($count >= $limit) {
        break;
    }
}

flock($fp, LOCK_UN);
fclose($fp);
