<?php
/**
 * Tom's Java Jive - Square Integration (cURL-based, no SDK required)
 */

function squareApi(string $method, string $path, array $body = []): array {
    $base = (defined('SQUARE_ENV') && SQUARE_ENV === 'sandbox')
        ? 'https://connect.squareupsandbox.com/v2'
        : 'https://connect.squareup.com/v2';

    $ch = curl_init($base . $path);
    $opts = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 30,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . SQUARE_ACCESS_TOKEN,
            'Square-Version: 2024-01-18',
        ],
    ];
    if ($method !== 'GET') {
        $opts[CURLOPT_CUSTOMREQUEST] = $method;
        $opts[CURLOPT_POSTFIELDS]    = json_encode($body ?: new stdClass());
    }
    curl_setopt_array($ch, $opts);

    $resp     = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $err      = curl_error($ch);
    curl_close($ch);

    if ($err) {
        throw new Exception('Square API connection error: ' . $err);
    }

    $decoded = json_decode($resp ?: '{}', true);

    if (!empty($decoded['errors'])) {
        $msg = $decoded['errors'][0]['detail'] ?? ($decoded['errors'][0]['code'] ?? 'Unknown Square error');
        throw new Exception($msg);
    }

    if ($httpCode >= 400) {
        throw new Exception('Square API error (HTTP ' . $httpCode . ')');
    }

    return $decoded;
}

/**
 * Create a payment. $sourceId is the nonce from the Web Payments SDK's tokenize() call.
 */
function squareCreatePayment(string $sourceId, float $amount, string $referenceId, array $options = []): array {
    $body = [
        'source_id'        => $sourceId,
        'idempotency_key'  => $referenceId . '_' . bin2hex(random_bytes(8)),
        'amount_money'     => [
            'amount'   => (int) round($amount * 100),
            'currency' => 'USD',
        ],
        'location_id'      => SQUARE_LOCATION_ID,
        'autocomplete'     => true,
        'reference_id'     => $referenceId,
    ];
    if (!empty($options['note'])) {
        $body['note'] = $options['note'];
    }
    return squareApi('POST', '/payments', $body);
}

function squareGetPayment(string $paymentId): array {
    return squareApi('GET', '/payments/' . $paymentId);
}

function isSquareConfigured(): bool {
    return defined('SQUARE_ACCESS_TOKEN') && defined('SQUARE_APP_ID') && defined('SQUARE_LOCATION_ID')
        && !empty(SQUARE_ACCESS_TOKEN) && !empty(SQUARE_APP_ID) && !empty(SQUARE_LOCATION_ID)
        && SQUARE_ACCESS_TOKEN !== 'REPLACE_ME';
}

/**
 * Square's webhook signature: base64(HMAC-SHA256(notification_url . raw_body, signature_key)).
 * The notification URL must exactly match what's configured in the Square Dashboard subscription.
 */
function verifySquareWebhookSignature(string $payload, string $signature, string $notificationUrl, string $signatureKey): bool {
    if (empty($signatureKey) || empty($signature)) {
        return false;
    }
    $expected = base64_encode(hash_hmac('sha256', $notificationUrl . $payload, $signatureKey, true));
    return hash_equals($expected, $signature);
}

/**
 * Single source of truth for "an order's Square payment resolved" — called from the
 * synchronous create-payment response, the webhook, and the reconciliation poll, so
 * loyalty points/emails are never awarded or sent more than once regardless of which
 * path resolves the order first.
 */
function markSquarePaymentResult(string $paymentId, ?string $orderId, string $status): void {
    $order = $orderId
        ? db()->fetch("SELECT * FROM orders WHERE order_id = :id", ['id' => $orderId])
        : db()->fetch("SELECT * FROM orders WHERE square_payment_id = :pid", ['pid' => $paymentId]);

    if (!$order) {
        return;
    }

    if ($status === 'COMPLETED') {
        if ($order['order_status'] !== 'confirmed') {
            db()->update('orders',
                [
                    'payment_status'    => 'paid',
                    'order_status'      => 'confirmed',
                    'square_payment_id' => $paymentId,
                    'payment_method'    => 'square',
                ],
                'order_id = :id',
                ['id' => $order['order_id']]
            );

            if (!empty($order['customer_id'])) {
                loyalty()->awardPoints(
                    $order['customer_id'],
                    (float) $order['total'],
                    'Order #' . $order['order_number'],
                    $order['order_id']
                );
            }

            if (!empty($order['wallet_amount_used']) && (float) $order['wallet_amount_used'] > 0 && !empty($order['customer_id'])) {
                $walletAmount = (float) $order['wallet_amount_used'];
                $cust = db()->fetch("SELECT wallet_balance FROM customers WHERE customer_id = :id", ['id' => $order['customer_id']]);
                if ($cust) {
                    $newBalance = max(0, (float) $cust['wallet_balance'] - $walletAmount);
                    db()->update('customers', ['wallet_balance' => $newBalance], 'customer_id = :id', ['id' => $order['customer_id']]);
                    db()->insert('wallet_transactions', [
                        'transaction_id' => generateId('wt_'),
                        'customer_id'    => $order['customer_id'],
                        'amount'         => -$walletAmount,
                        'balance_after'  => $newBalance,
                        'type'           => 'purchase',
                        'description'    => 'Applied to Order #' . $order['order_number'],
                    ]);
                }
            }

            $updated = db()->fetch("SELECT * FROM orders WHERE order_id = :id", ['id' => $order['order_id']]);
            if ($updated) {
                emailService()->sendOrderConfirmation($updated);
            }
        }
    } elseif (in_array($status, ['FAILED', 'CANCELED'], true)) {
        db()->update('orders',
            ['payment_status' => 'failed'],
            'order_id = :id',
            ['id' => $order['order_id']]
        );
    }
}
