<?php

declare(strict_types=1);

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

const CLOUDFLARE_API_TOKEN = 'cfut_e8cftsaHaZlAGrJ4b0rEM1dLDXNk720uuyg5JEOE61cd23a0';
const CLOUDFLARE_ZONE_NAME = 'picobox.it';
const CLOUDFLARE_RECORD_NAME = 'webcamsassilive.picobox.it';
const CLOUDFLARE_RECORD_TTL = 60;
const CLOUDFLARE_RECORD_PROXIED = false;

function respond(int $status, array $payload): never
{
    http_response_code($status);
    echo json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
    exit;
}

function cloudflare_request(string $method, string $url, ?array $payload = null): array
{
    $ch = curl_init($url);
    if ($ch === false) {
        throw new RuntimeException('Unable to initialize curl');
    }

    $headers = [
        'Authorization: Bearer ' . CLOUDFLARE_API_TOKEN,
        'Content-Type: application/json',
    ];

    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => $headers,
    ]);

    if ($payload !== null) {
        $json = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        if ($json === false) {
            throw new RuntimeException('Unable to encode JSON payload');
        }
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    }

    $body = curl_exec($ch);
    if ($body === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException('Cloudflare request failed: ' . $error);
    }

    $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    $decoded = json_decode($body, true);
    if (!is_array($decoded)) {
        throw new RuntimeException('Invalid Cloudflare response: ' . $body);
    }

    if ($status < 200 || $status >= 300 || empty($decoded['success'])) {
        throw new RuntimeException('Cloudflare API error: ' . $body);
    }

    return $decoded['result'];
}

function cf_get_zone_id(string $zoneName): string
{
    $query = http_build_query(['name' => $zoneName]);
    $result = cloudflare_request('GET', 'https://api.cloudflare.com/client/v4/zones?' . $query);
    if (empty($result[0]['id'])) {
        throw new RuntimeException('Zone not found: ' . $zoneName);
    }
    return (string) $result[0]['id'];
}

function cf_list_records(string $zoneId, string $fqdn): array
{
    $query = http_build_query(['name' => $fqdn]);
    return cloudflare_request(
        'GET',
        'https://api.cloudflare.com/client/v4/zones/' . rawurlencode($zoneId) . '/dns_records?' . $query
    );
}

function cf_delete_record(string $zoneId, string $recordId): void
{
    cloudflare_request(
        'DELETE',
        'https://api.cloudflare.com/client/v4/zones/' . rawurlencode($zoneId) . '/dns_records/' . rawurlencode($recordId)
    );
}

function cf_create_record(string $zoneId, string $fqdn, string $ip): array
{
    return cloudflare_request(
        'POST',
        'https://api.cloudflare.com/client/v4/zones/' . rawurlencode($zoneId) . '/dns_records',
        [
            'type' => 'A',
            'name' => $fqdn,
            'content' => $ip,
            'ttl' => CLOUDFLARE_RECORD_TTL,
            'proxied' => CLOUDFLARE_RECORD_PROXIED,
            'comment' => 'managed-by webcam update.php',
        ]
    );
}

function cf_update_record(string $zoneId, string $recordId, string $fqdn, string $ip): array
{
    return cloudflare_request(
        'PUT',
        'https://api.cloudflare.com/client/v4/zones/' . rawurlencode($zoneId) . '/dns_records/' . rawurlencode($recordId),
        [
            'type' => 'A',
            'name' => $fqdn,
            'content' => $ip,
            'ttl' => CLOUDFLARE_RECORD_TTL,
            'proxied' => CLOUDFLARE_RECORD_PROXIED,
            'comment' => 'managed-by webcam update.php',
        ]
    );
}

$ip = $_REQUEST['ip'] ?? '';
if ($ip === '') {
    respond(400, [
        'ok' => false,
        'error' => 'Missing ip parameter',
    ]);
}

if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
    respond(400, [
        'ok' => false,
        'error' => 'Invalid IPv4 address',
        'ip' => $ip,
    ]);
}

try {
    $zoneId = cf_get_zone_id(CLOUDFLARE_ZONE_NAME);
    $records = cf_list_records($zoneId, CLOUDFLARE_RECORD_NAME);

    $aRecords = [];
    foreach ($records as $record) {
        if (($record['type'] ?? '') === 'A') {
            $aRecords[] = $record;
            continue;
        }

        if (!empty($record['id'])) {
            cf_delete_record($zoneId, (string) $record['id']);
        }
    }

    if (count($aRecords) > 1) {
        foreach ($aRecords as $record) {
            if (!empty($record['id'])) {
                cf_delete_record($zoneId, (string) $record['id']);
            }
        }
        $aRecords = [];
    }

    if ($aRecords === []) {
        $created = cf_create_record($zoneId, CLOUDFLARE_RECORD_NAME, $ip);
        respond(200, [
            'ok' => true,
            'action' => 'created',
            'name' => CLOUDFLARE_RECORD_NAME,
            'ip' => $ip,
            'record_id' => $created['id'] ?? null,
        ]);
    }

    $record = $aRecords[0];
    $currentIp = (string) ($record['content'] ?? '');
    $currentTtl = (int) ($record['ttl'] ?? 0);
    $currentProxied = (bool) ($record['proxied'] ?? false);

    if ($currentIp === $ip && $currentTtl === CLOUDFLARE_RECORD_TTL && $currentProxied === CLOUDFLARE_RECORD_PROXIED) {
        respond(200, [
            'ok' => true,
            'action' => 'unchanged',
            'name' => CLOUDFLARE_RECORD_NAME,
            'ip' => $ip,
            'record_id' => $record['id'] ?? null,
        ]);
    }

    $updated = cf_update_record($zoneId, (string) $record['id'], CLOUDFLARE_RECORD_NAME, $ip);
    respond(200, [
        'ok' => true,
        'action' => 'updated',
        'name' => CLOUDFLARE_RECORD_NAME,
        'ip' => $ip,
        'record_id' => $updated['id'] ?? ($record['id'] ?? null),
    ]);
} catch (Throwable $e) {
    respond(500, [
        'ok' => false,
        'error' => $e->getMessage(),
    ]);
}
