<?php
declare(strict_types=1);

/**
 * POST /c2c/payments/webhook.php
 *
 * Empfängt PayPal Webhook-Events. Zweck:
 *   - Backup für Capture (falls Browser-Capture nicht durchkommt)
 *   - Refunds, Reversals, Disputes erfassen
 *
 * Signatur wird gegen PayPal /v1/notifications/verify-webhook-signature geprüft.
 *
 * Webhook-URL in PayPal-Dashboard registrieren:
 *   https://christianwehrli.ca/c2c/payments/webhook.php
 *
 * Events abonnieren mindestens:
 *   - PAYMENT.CAPTURE.COMPLETED
 *   - PAYMENT.CAPTURE.REFUNDED
 *   - PAYMENT.CAPTURE.REVERSED
 *   - PAYMENT.CAPTURE.DENIED
 */

require_once __DIR__ . '/paypal-client.php';
$config = require __DIR__ . '/_config.php';

$rawBody = file_get_contents('php://input');

// Headers normalisieren auf lowercase
$headers = [];
foreach (getallheaders() ?: [] as $name => $value) {
    $headers[strtolower($name)] = $value;
}

try {
    $sandbox    = (bool)$config['paypal']['sandbox'];
    $webhookId  = $sandbox ? $config['paypal']['sandbox_webhook_id'] : $config['paypal']['live_webhook_id'];

    if (empty($webhookId) || $webhookId === 'NACH_WEBHOOK_REGISTRATION_EINFUEGEN') {
        http_response_code(503);
        error_log('[c2c webhook] webhook_id nicht konfiguriert');
        exit('webhook not configured');
    }

    $paypal = new PayPalClient(
        $sandbox ? $config['paypal']['sandbox_client_id']     : $config['paypal']['live_client_id'],
        $sandbox ? $config['paypal']['sandbox_client_secret'] : $config['paypal']['live_client_secret'],
        $sandbox
    );

    // Signatur-Prüfung
    if (!$paypal->verifyWebhookSignature($headers, $rawBody, $webhookId)) {
        http_response_code(401);
        error_log('[c2c webhook] Signatur ungültig');
        exit('invalid signature');
    }

    $event     = json_decode($rawBody, true) ?: [];
    $eventType = (string)($event['event_type'] ?? '');
    $eventId   = (string)($event['id']         ?? '');

    $pdo = new PDO(
        "mysql:host={$config['db']['host']};dbname={$config['db']['name']};charset=utf8mb4",
        $config['db']['user'],
        $config['db']['password'],
        [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]
    );

    switch ($eventType) {

        case 'PAYMENT.CAPTURE.COMPLETED':
            $captureId = (string)($event['resource']['id'] ?? '');
            $orderId   = extractOrderIdFromCapture($event);

            if ($orderId === '') {
                error_log("[c2c webhook] $eventType: order_id nicht ableitbar (event $eventId)");
                break;
            }

            $stmt = $pdo->prepare('SELECT id, client_id, status FROM payments WHERE paypal_order_id = ? LIMIT 1');
            $stmt->execute([$orderId]);
            $payment = $stmt->fetch();

            if ($payment && $payment['status'] !== 'completed') {
                $pdo->beginTransaction();
                $pdo->prepare('UPDATE payments SET status = "completed", paypal_capture_id = ?, raw_response = ? WHERE id = ?')
                    ->execute([$captureId, $rawBody, $payment['id']]);
                $pdo->prepare('UPDATE clients SET status = "paid", updated_at = NOW() WHERE id = ?')
                    ->execute([$payment['client_id']]);
                $pdo->prepare('INSERT INTO aktivitaeten (klient_id, aktion, beschreibung, erstellt_am) VALUES (?, ?, ?, NOW())')
                    ->execute([$payment['client_id'], 'webhook_capture_completed', 'Capture: ' . $captureId]);
                $pdo->commit();
            }
            break;

        case 'PAYMENT.CAPTURE.REFUNDED':
        case 'PAYMENT.CAPTURE.REVERSED':
            // resource.id bei Refund = Refund-ID; capture-ID steckt in links/up
            $captureId = extractCaptureIdFromRefund($event);

            if ($captureId === '') {
                error_log("[c2c webhook] $eventType: capture_id nicht ableitbar (event $eventId)");
                break;
            }

            $stmt = $pdo->prepare('SELECT id, client_id FROM payments WHERE paypal_capture_id = ? LIMIT 1');
            $stmt->execute([$captureId]);
            $payment = $stmt->fetch();

            if ($payment) {
                $pdo->prepare('UPDATE payments SET status = "refunded", raw_response = ? WHERE id = ?')
                    ->execute([$rawBody, $payment['id']]);
                $pdo->prepare('INSERT INTO aktivitaeten (klient_id, aktion, beschreibung, erstellt_am) VALUES (?, ?, ?, NOW())')
                    ->execute([$payment['client_id'], 'zahlung_erstattet', "Capture: $captureId · $eventType"]);
            }
            break;

        case 'PAYMENT.CAPTURE.DENIED':
            $orderId = extractOrderIdFromCapture($event);
            if ($orderId !== '') {
                $stmt = $pdo->prepare('UPDATE payments SET status = "failed", raw_response = ? WHERE paypal_order_id = ?');
                $stmt->execute([$rawBody, $orderId]);
            }
            break;

        default:
            error_log("[c2c webhook] ignoriert: $eventType (event $eventId)");
            break;
    }

    http_response_code(200);
    echo 'ok';

} catch (Throwable $e) {
    http_response_code(500);
    error_log('[c2c webhook] ' . $e->getMessage());
    echo 'error';
}

// ---------------- Helpers ----------------

function extractOrderIdFromCapture(array $event): string
{
    // Variante 1: resource.supplementary_data.related_ids.order_id
    $orderId = $event['resource']['supplementary_data']['related_ids']['order_id'] ?? '';
    if ($orderId) return (string)$orderId;

    // Variante 2: aus links[rel=up]
    foreach (($event['resource']['links'] ?? []) as $link) {
        if (($link['rel'] ?? '') === 'up' && preg_match('#/orders/([^/]+)#', (string)$link['href'], $m)) {
            return $m[1];
        }
    }
    return '';
}

function extractCaptureIdFromRefund(array $event): string
{
    foreach (($event['resource']['links'] ?? []) as $link) {
        if (($link['rel'] ?? '') === 'up' && preg_match('#/captures/([^/]+)#', (string)$link['href'], $m)) {
            return $m[1];
        }
    }
    return '';
}
