<?php
/**
 * admanager/save.php — Authenticated ad CRUD API
 * ─────────────────────────────────────────────────────────────────────────────
 * All requests require the X-Ad-Key header matching ADS_ADMIN_KEY in config.php.
 *
 * POST   save.php              body: ad object          → create
 * PUT    save.php?id=<id>      body: partial ad fields  → update
 * DELETE save.php?id=<id>                               → delete
 * POST   save.php?action=reorder  body: {ids:[…]}       → reorder
 * POST   save.php?action=toggle   body: {id,active}     → toggle active
 */

require_once __DIR__ . '/config.php';

header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');

function ad_ok(mixed $data, int $code = 200): never {
    http_response_code($code);
    echo json_encode(['ok' => true, 'data' => $data], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
    exit;
}
function ad_err(string $msg, int $code = 400): never {
    http_response_code($code);
    echo json_encode(['ok' => false, 'error' => $msg]);
    exit;
}

// ── Auth ──────────────────────────────────────────────────────────────────────
$supplied = trim($_SERVER['HTTP_X_AD_KEY'] ?? $_SERVER['HTTP_X_Ad_Key'] ?? ($_GET['ad_key'] ?? ''));
if ($supplied === '') ad_err('Missing X-Ad-Key header.', 401);
if (!hash_equals(ADS_ADMIN_KEY, $supplied)) ad_err('Invalid key.', 403);

// ── Data helpers ──────────────────────────────────────────────────────────────
function load_ads(): array {
    if (!file_exists(ADS_JSON_PATH)) return [];
    $raw = json_decode(file_get_contents(ADS_JSON_PATH), true);
    return is_array($raw) ? $raw : [];
}

function save_ads(array $ads): void {
    $json = json_encode(array_values($ads), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
    $tmp  = ADS_JSON_PATH . '.tmp.' . getmypid();
    if (file_put_contents($tmp, $json, LOCK_EX) === false) ad_err('Write failed.', 500);
    if (!rename($tmp, ADS_JSON_PATH)) { @unlink($tmp); ad_err('Rename failed.', 500); }
}

function find_ad(array $ads, string $id): int|false {
    foreach ($ads as $i => $a) { if (($a['id'] ?? '') === $id) return $i; }
    return false;
}

function new_id(array $ads): string {
    $existing = array_column($ads, 'id');
    do { $id = 'ad_' . strtolower(bin2hex(random_bytes(4))); } while (in_array($id, $existing));
    return $id;
}

function read_body(): array {
    $raw = file_get_contents('php://input');
    if (!$raw) return [];
    $d = json_decode($raw, true);
    if (!is_array($d)) ad_err('Body must be valid JSON.');
    return $d;
}

function sanitise_ad(array $in, bool $require_fields = true): array {
    $errors = [];
    if ($require_fields) {
        if (trim($in['title'] ?? '') === '') $errors[] = '"title" is required.';
        if (trim($in['placement'] ?? '') === '') $errors[] = '"placement" is required.';
        $valid_placements = array_keys(unserialize(ADS_PLACEMENTS));
        if (!in_array($in['placement'] ?? '', $valid_placements)) $errors[] = 'Invalid placement.';
    }
    if ($errors) ad_err(implode(' ', $errors));

    $out = [];
    if (isset($in['title']))      $out['title']      = trim((string)$in['title']);
    if (isset($in['placement']))  $out['placement']  = trim((string)$in['placement']);
    if (isset($in['click_url']))  $out['click_url']  = trim((string)$in['click_url']);
    if (isset($in['active']))     $out['active']     = (bool)$in['active'];
    if (isset($in['note']))       $out['note']       = trim((string)$in['note']);

    // sizes: array of {size_key, media_url, media_type, custom_w, custom_h}
    if (isset($in['sizes']) && is_array($in['sizes'])) {
        $clean_sizes = [];
        foreach ($in['sizes'] as $s) {
            if (empty($s['size_key']) || empty($s['media_url'])) continue;
            $clean_sizes[] = [
                'size_key'   => trim((string)$s['size_key']),
                'media_url'  => trim((string)$s['media_url']),
                'media_type' => in_array($s['media_type'] ?? '', ['image','video']) ? $s['media_type'] : 'image',
                'custom_w'   => isset($s['custom_w']) ? (int)$s['custom_w'] : null,
                'custom_h'   => isset($s['custom_h']) ? (int)$s['custom_h'] : null,
            ];
        }
        $out['sizes'] = $clean_sizes;
    }
    return $out;
}

// ── Routing ───────────────────────────────────────────────────────────────────
$method = $_SERVER['REQUEST_METHOD'];
$id     = trim($_GET['id']     ?? '');
$action = trim($_GET['action'] ?? '');

// Toggle active
if ($method === 'POST' && $action === 'toggle') {
    $body = read_body();
    $tid  = trim((string)($body['id'] ?? ''));
    $ads  = load_ads();
    $idx  = find_ad($ads, $tid);
    if ($idx === false) ad_err("Ad \"$tid\" not found.", 404);
    $ads[$idx]['active'] = (bool)($body['active'] ?? !$ads[$idx]['active']);
    save_ads($ads);
    ad_ok($ads[$idx]);
}

// Reorder
if ($method === 'POST' && $action === 'reorder') {
    $body = read_body();
    if (!isset($body['ids']) || !is_array($body['ids'])) ad_err('"ids" required.');
    $ads     = load_ads();
    $indexed = [];
    foreach ($ads as $a) $indexed[$a['id']] = $a;
    $reordered = [];
    foreach ($body['ids'] as $rid) {
        if (!isset($indexed[$rid])) ad_err("Unknown id \"$rid\".");
        $reordered[] = $indexed[$rid];
        unset($indexed[$rid]);
    }
    foreach ($indexed as $a) $reordered[] = $a;
    save_ads($reordered);
    ad_ok(['reordered' => count($reordered)]);
}

// Create
if ($method === 'POST') {
    $body  = read_body();
    $clean = sanitise_ad($body, true);
    $ads   = load_ads();
    $ad    = array_merge([
        'id'        => new_id($ads),
        'title'     => '',
        'placement' => 'above',
        'click_url' => '',
        'active'    => true,
        'note'      => '',
        'sizes'     => [],
        'created'   => (new DateTime('now', new DateTimeZone('UTC')))->format(DateTime::ATOM),
    ], $clean);
    array_unshift($ads, $ad);
    save_ads($ads);
    ad_ok($ad, 201);
}

// Update
if ($method === 'PUT') {
    if ($id === '') ad_err('?id= required for PUT.');
    $ads = load_ads();
    $idx = find_ad($ads, $id);
    if ($idx === false) ad_err("Ad \"$id\" not found.", 404);
    $body  = read_body();
    $clean = sanitise_ad($body, false);
    $ads[$idx] = array_merge($ads[$idx], $clean);
    $ads[$idx]['id'] = $id;
    save_ads($ads);
    ad_ok($ads[$idx]);
}

// Delete
if ($method === 'DELETE') {
    if ($id === '') ad_err('?id= required for DELETE.');
    $ads = load_ads();
    $idx = find_ad($ads, $id);
    if ($idx === false) ad_err("Ad \"$id\" not found.", 404);
    $deleted = $ads[$idx];
    array_splice($ads, $idx, 1);
    save_ads($ads);
    ad_ok($deleted);
}

ad_err('Method not allowed.', 405);
