<?php
/**
 * Stripe webhook receiver.
 *
 * Updates HubSpot Order payment_status based on Checkout Session events.
 */

require __DIR__ . '/hubspot-config.php';
require __DIR__ . '/stripe-config.php';
require __DIR__ . '/includes/hubspot-api.php';
require __DIR__ . '/includes/stripe-api.php';

header('Content-Type: text/plain; charset=utf-8');

function log_message(string $level, string $message, array $context = []): void
{
    $primaryDir = __DIR__ . '/../logs';
    $logDir = $primaryDir;
    if (!is_dir($logDir)) {
        @mkdir($logDir, 0775, true);
    }
    if (!is_dir($logDir) || !is_writable($logDir)) {
        $fallback = rtrim((string) sys_get_temp_dir(), "\\/") . DIRECTORY_SEPARATOR . 'hubspot-shopify-logs';
        $logDir = $fallback;
        if (!is_dir($logDir)) {
            @mkdir($logDir, 0775, true);
        }
    }

    $logFile = rtrim($logDir, "\\/") . DIRECTORY_SEPARATOR . 'stripe-webhook.log';
    $ts = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s\Z');
    $ctx = '';
    if (!empty($context)) {
        $ctx = ' ' . json_encode($context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }
    $line = $ts . ' [' . $level . '] ' . $message . $ctx . PHP_EOL;
    @file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
    @error_log('[stripe-webhook] ' . trim($line));
}

$rawBody = file_get_contents('php://input');
if (!is_string($rawBody)) {
    $rawBody = '';
}

$sigHeader = isset($_SERVER['HTTP_STRIPE_SIGNATURE']) ? (string) $_SERVER['HTTP_STRIPE_SIGNATURE'] : null;
$secret = defined('STRIPE_WEBHOOK_SECRET') ? (string) STRIPE_WEBHOOK_SECRET : '';
if (!stripe_verify_webhook_signature($rawBody, $sigHeader, $secret)) {
    log_message('warn', 'Invalid Stripe signature');
    http_response_code(401);
    exit;
}

$event = json_decode($rawBody, true);
if (!is_array($event)) {
    log_message('error', 'Invalid JSON payload');
    http_response_code(400);
    exit;
}

$type = (string) ($event['type'] ?? '');
$obj = $event['data']['object'] ?? null;
if (!is_array($obj)) {
    log_message('info', 'Ignored: no object', ['type' => $type]);
    http_response_code(200);
    exit;
}

log_message('info', 'Event received', [
    'type' => $type,
    'id' => (string) ($event['id'] ?? ''),
]);

// We sync statuses from both Checkout Session events (good for card) and PaymentIntent events (needed for ACH async).
$supported = (
    strpos($type, 'checkout.session.') === 0 ||
    strpos($type, 'payment_intent.') === 0
);
if (!$supported) {
    log_message('info', 'Ignored event type', ['type' => $type]);
    http_response_code(200);
    exit;
}

$objectId = (string) ($obj['id'] ?? '');
$metadata = $obj['metadata'] ?? [];
$shopifyOrderId = '';
if (is_array($metadata)) {
    $shopifyOrderId = (string) ($metadata['shopify_order_id'] ?? '');
}

if ($shopifyOrderId === '') {
    log_message('warn', 'Missing shopify_order_id metadata', ['type' => $type, 'objectId' => $objectId]);
    http_response_code(200);
    exit;
}

$hubspotOrderId = hubspot_find_order_id_by_shopify_order($shopifyOrderId);
if ($hubspotOrderId === null) {
    log_message('warn', 'HubSpot Order not found', ['shopifyOrderId' => $shopifyOrderId, 'type' => $type]);
    http_response_code(200);
    exit;
}

$newStatus = null;
// Checkout Session mapping:
// - For ACH, `checkout.session.completed` is often still pending; so use payment_status when available.
if ($type === 'checkout.session.completed') {
    $ps = (string) ($obj['payment_status'] ?? '');
    if ($ps === 'paid') {
        $newStatus = 'paid';
    } elseif ($ps !== '') {
        $newStatus = 'processing';
    } else {
        $newStatus = 'processing';
    }
} elseif ($type === 'checkout.session.async_payment_succeeded') {
    // For async payment methods (including bank debits), Stripe emits this when funds are confirmed.
    $newStatus = 'paid';
} elseif ($type === 'checkout.session.expired') {
    $newStatus = 'expired';
} elseif ($type === 'checkout.session.async_payment_failed') {
    $newStatus = 'failed';
}

// PaymentIntent mapping (more reliable for ACH):
if ($type === 'payment_intent.succeeded') {
    $newStatus = 'paid';
} elseif ($type === 'payment_intent.payment_failed' || $type === 'payment_intent.canceled') {
    $newStatus = 'failed';
} elseif ($type === 'payment_intent.processing') {
    $newStatus = 'processing';
}

if ($newStatus === null) {
    log_message('info', 'No status mapping for event', ['type' => $type, 'shopifyOrderId' => $shopifyOrderId]);
    http_response_code(200);
    exit;
}

$patch = [
    HUBSPOT_ORDER_PROPERTY_PAYMENT_STATUS => $newStatus,
];
// For checkout.session.* this is a session id; for payment_intent.* it's a PaymentIntent id.
// We still store it in the session-id field for traceability (optional).
if ($objectId !== '') {
    $patch[HUBSPOT_ORDER_PROPERTY_STRIPE_CHECKOUT_SESSION_ID] = $objectId;
}

$patched = hubspot_update_order_properties($hubspotOrderId, $patch);
if (!$patched['ok']) {
    log_message('error', 'HubSpot Order update failed', [
        'shopifyOrderId' => $shopifyOrderId,
        'hubspotOrderId' => $hubspotOrderId,
        'type' => $type,
        'status' => $patched['status'] ?? null,
        'error' => $patched['error'] ?? null,
    ]);
    http_response_code(502);
    exit;
}

log_message('info', 'Updated HubSpot payment status', [
    'shopifyOrderId' => $shopifyOrderId,
    'hubspotOrderId' => $hubspotOrderId,
    'type' => $type,
    'payment_status' => $newStatus,
    'objectId' => $objectId,
]);

http_response_code(200);
echo "OK\n";

