<?php
declare(strict_types=1);

/**
 * KAPVOE Stripe Checkout helpers
 * Plain PHP + cURL. No Composer dependency required.
 */

function kapvoe_load_config(): array {
    $configFile = __DIR__ . '/config.php';
    if (!file_exists($configFile)) {
        http_response_code(500);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'ok' => false,
            'error' => 'Falta el fitxer config.php'
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    /** @var array $config */
    $config = require $configFile;
    return $config;
}

function kapvoe_json_input(): array {
    $raw = file_get_contents('php://input') ?: '{}';
    $decoded = json_decode($raw, true);
    return is_array($decoded) ? $decoded : [];
}

function kapvoe_json_response(array $payload, int $status = 200): never {
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function kapvoe_require_post(): void {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        kapvoe_json_response(['ok' => false, 'error' => 'Mètode no permès'], 405);
    }
}

function kapvoe_validate_required(array $data, array $fields): array {
    $errors = [];
    foreach ($fields as $field) {
        if (!isset($data[$field]) || trim((string)$data[$field]) === '') {
            $errors[$field] = 'Camp obligatori';
        }
    }
    return $errors;
}

function kapvoe_orders_csv_path(array $config): string {
    $dir = rtrim((string)$config['orders_storage_dir'], DIRECTORY_SEPARATOR);
    if (!is_dir($dir)) {
        mkdir($dir, 0775, true);
    }
    return $dir . DIRECTORY_SEPARATOR . 'orders.csv';
}

function kapvoe_orders_csv_header(): array {
    return [
        'order_id',
        'created_at',
        'product_code',
        'product_name',
        'product_image_url',
        'unit_price',
        'quantity',
        'subtotal',
        'shipping_method',
        'shipping_cost',
        'total_amount',
        'customer_name',
        'address',
        'postal_code',
        'city',
        'province',
        'phone',
        'email',
        'analytics_session_id',
        'analytics_page_url',
        'analytics_referrer',
        'analytics_user_agent',
        'analytics_utm_source',
        'analytics_utm_medium',
        'analytics_utm_campaign',
        'analytics_device_type',
        'analytics_page_type',
        'payment_status',
        'stripe_session_id',
        'payment_intent_id',
        'stock_updated',
        'stock_updated_at',
        'webhook_processed_at',
        'customer_email_sent',
        'admin_email_sent',
        'email_notifications_sent_at',
    ];
}

function kapvoe_upgrade_orders_csv_schema(array $config): void {
    $path = kapvoe_orders_csv_path($config);
    if (!file_exists($path)) {
        return;
    }

    $fh = fopen($path, 'rb');
    if (!$fh) {
        return;
    }

    $rows = [];
    while (($row = fgetcsv($fh, 0, ';')) !== false) {
        $rows[] = $row;
    }
    fclose($fh);

    if (!$rows) {
        return;
    }

    $targetHeader = kapvoe_orders_csv_header();
    $currentHeader = $rows[0];

    if ($currentHeader === $targetHeader) {
        return;
    }

    $legacyHeader = [
        'order_id',
        'created_at',
        'product_code',
        'product_name',
        'unit_price',
        'quantity',
        'customer_name',
        'address',
        'postal_code',
        'city',
        'province',
        'phone',
        'email',
        'payment_status',
        'stripe_session_id',
        'payment_intent_id',
        'stock_updated',
        'stock_updated_at',
        'webhook_processed_at',
    ];
    $headerWithoutImage = [
        'order_id',
        'created_at',
        'product_code',
        'product_name',
        'unit_price',
        'quantity',
        'subtotal',
        'shipping_method',
        'shipping_cost',
        'total_amount',
        'customer_name',
        'address',
        'postal_code',
        'city',
        'province',
        'phone',
        'email',
        'payment_status',
        'stripe_session_id',
        'payment_intent_id',
        'stock_updated',
        'stock_updated_at',
        'webhook_processed_at',
    ];

    $newRows = [$targetHeader];

    for ($i = 1; $i < count($rows); $i++) {
        $row = $rows[$i];

        if ($currentHeader === $legacyHeader) {
            if (count($row) === count($legacyHeader)) {
                $normalized = [
                    $row[0] ?? '',
                    $row[1] ?? '',
                    $row[2] ?? '',
                    $row[3] ?? '',
                    '',
                    $row[4] ?? '',
                    $row[5] ?? '',
                    '',
                    '',
                    '',
                    '',
                    $row[6] ?? '',
                    $row[7] ?? '',
                    $row[8] ?? '',
                    $row[9] ?? '',
                    $row[10] ?? '',
                    $row[11] ?? '',
                    $row[12] ?? '',
                    $row[13] ?? '',
                    $row[14] ?? '',
                    $row[15] ?? '',
                    $row[16] ?? '',
                    $row[17] ?? '',
                    $row[18] ?? '',
                ];
            } elseif (count($row) === count($legacyHeader) + 1) {
                $normalized = [
                    $row[0] ?? '',
                    $row[1] ?? '',
                    $row[2] ?? '',
                    $row[3] ?? '',
                    $row[4] ?? '',
                    $row[5] ?? '',
                    $row[6] ?? '',
                    '',
                    '',
                    '',
                    '',
                    $row[7] ?? '',
                    $row[8] ?? '',
                    $row[9] ?? '',
                    $row[10] ?? '',
                    $row[11] ?? '',
                    $row[12] ?? '',
                    $row[13] ?? '',
                    $row[14] ?? '',
                    $row[15] ?? '',
                    $row[16] ?? '',
                    $row[17] ?? '',
                    $row[18] ?? '',
                    $row[19] ?? '',
                ];
            } else {
                $normalized = array_pad(array_slice($row, 0, count($targetHeader)), count($targetHeader), '');
            }
        } elseif ($currentHeader === $headerWithoutImage) {
            if (count($row) === count($headerWithoutImage)) {
                $normalized = [
                    $row[0] ?? '',
                    $row[1] ?? '',
                    $row[2] ?? '',
                    $row[3] ?? '',
                    '',
                    $row[4] ?? '',
                    $row[5] ?? '',
                    $row[6] ?? '',
                    $row[7] ?? '',
                    $row[8] ?? '',
                    $row[9] ?? '',
                    $row[10] ?? '',
                    $row[11] ?? '',
                    $row[12] ?? '',
                    $row[13] ?? '',
                    $row[14] ?? '',
                    $row[15] ?? '',
                    $row[16] ?? '',
                    $row[17] ?? '',
                    $row[18] ?? '',
                    $row[19] ?? '',
                    $row[20] ?? '',
                    $row[21] ?? '',
                    $row[22] ?? '',
                ];
            } elseif (count($row) === count($headerWithoutImage) + 1) {
                $normalized = [
                    $row[0] ?? '',
                    $row[1] ?? '',
                    $row[2] ?? '',
                    $row[3] ?? '',
                    $row[4] ?? '',
                    $row[5] ?? '',
                    $row[6] ?? '',
                    $row[7] ?? '',
                    $row[8] ?? '',
                    $row[9] ?? '',
                    $row[10] ?? '',
                    $row[11] ?? '',
                    $row[12] ?? '',
                    $row[13] ?? '',
                    $row[14] ?? '',
                    $row[15] ?? '',
                    $row[16] ?? '',
                    $row[17] ?? '',
                    $row[18] ?? '',
                    $row[19] ?? '',
                    $row[20] ?? '',
                    $row[21] ?? '',
                    $row[22] ?? '',
                    $row[23] ?? '',
                ];
            } else {
                $normalized = array_pad(array_slice($row, 0, count($targetHeader)), count($targetHeader), '');
            }
        } else {
            $assoc = [];
            $limit = min(count($currentHeader), count($row));
            for ($j = 0; $j < $limit; $j++) {
                $assoc[$currentHeader[$j]] = $row[$j];
            }

            $normalized = [];
            foreach ($targetHeader as $column) {
                $normalized[] = $assoc[$column] ?? '';
            }
        }

        $newRows[] = $normalized;
    }

    $fh = fopen($path, 'wb');
    if (!$fh) {
        return;
    }

    foreach ($newRows as $row) {
        fputcsv($fh, $row, ';');
    }
    fclose($fh);
}

function kapvoe_append_order(array $config, array $order): void {
    $path = kapvoe_orders_csv_path($config);
    kapvoe_upgrade_orders_csv_schema($config);
    $exists = file_exists($path);
    $fh = fopen($path, 'ab');
    if (!$fh) {
        throw new RuntimeException('No s\'ha pogut obrir orders.csv');
    }

    if (!$exists) {
        fputcsv($fh, kapvoe_orders_csv_header(), ';');
    }

    $row = [];
    foreach (kapvoe_orders_csv_header() as $column) {
        $row[] = $order[$column] ?? '';
    }
    fputcsv($fh, $row, ';');
    fclose($fh);
}

function kapvoe_update_order_status(array $config, string $sessionId, string $newStatus, string $paymentIntent = ''): void {
    $path = kapvoe_orders_csv_path($config);
    kapvoe_upgrade_orders_csv_schema($config);
    if (!file_exists($path)) {
        return;
    }

    $rows = [];
    $fh = fopen($path, 'rb');
    if (!$fh) {
        return;
    }
    while (($row = fgetcsv($fh, 0, ';')) !== false) {
        $rows[] = $row;
    }
    fclose($fh);

    if (count($rows) < 2) {
        return;
    }

    $header = $rows[0];
    $sessionIdx = array_search('stripe_session_id', $header, true);
    $statusIdx = array_search('payment_status', $header, true);
    $intentIdx = array_search('payment_intent_id', $header, true);

    if ($sessionIdx === false || $statusIdx === false) {
        return;
    }

    for ($i = 1; $i < count($rows); $i++) {
        while (count($rows[$i]) < count($header)) {
            $rows[$i][] = '';
        }

        if (($rows[$i][$sessionIdx] ?? '') === $sessionId) {
            $rows[$i][$statusIdx] = $newStatus;
            if ($intentIdx !== false) {
                $rows[$i][$intentIdx] = $paymentIntent;
            }
            break;
        }
    }

    $fh = fopen($path, 'wb');
    if (!$fh) {
        return;
    }
    foreach ($rows as $row) {
        while (count($row) < count($header)) {
            $row[] = '';
        }
        fputcsv($fh, $row, ';');
    }
    fclose($fh);
}

function kapvoe_get_order_by_session_id(array $config, string $sessionId): ?array {
    $path = kapvoe_orders_csv_path($config);
    kapvoe_upgrade_orders_csv_schema($config);
    if (!file_exists($path)) {
        return null;
    }

    $fh = fopen($path, 'rb');
    if (!$fh) {
        return null;
    }

    $header = fgetcsv($fh, 0, ';');
    if (!$header) {
        fclose($fh);
        return null;
    }

    while (($row = fgetcsv($fh, 0, ';')) !== false) {
        while (count($row) < count($header)) {
            $row[] = '';
        }
        $assoc = array_combine($header, $row);
        if (($assoc['stripe_session_id'] ?? '') === $sessionId) {
            fclose($fh);
            return $assoc ?: null;
        }
    }

    fclose($fh);
    return null;
}

function kapvoe_mark_order_stock_updated(array $config, string $sessionId): void {
    $path = kapvoe_orders_csv_path($config);
    kapvoe_upgrade_orders_csv_schema($config);
    if (!file_exists($path)) {
        return;
    }

    $rows = [];
    $fh = fopen($path, 'rb');
    if (!$fh) {
        return;
    }

    while (($row = fgetcsv($fh, 0, ';')) !== false) {
        $rows[] = $row;
    }
    fclose($fh);

    if (count($rows) < 1) {
        return;
    }

    $header = $rows[0];

    $sessionIdx = array_search('stripe_session_id', $header, true);
    $stockUpdatedIdx = array_search('stock_updated', $header, true);
    $stockUpdatedAtIdx = array_search('stock_updated_at', $header, true);
    $webhookProcessedIdx = array_search('webhook_processed_at', $header, true);

    if ($sessionIdx === false) {
        return;
    }

    if ($stockUpdatedIdx === false) {
        $header[] = 'stock_updated';
        $stockUpdatedIdx = count($header) - 1;
    }

    if ($stockUpdatedAtIdx === false) {
        $header[] = 'stock_updated_at';
        $stockUpdatedAtIdx = count($header) - 1;
    }

    if ($webhookProcessedIdx === false) {
        $header[] = 'webhook_processed_at';
        $webhookProcessedIdx = count($header) - 1;
    }

    $rows[0] = $header;
    $now = date('Y-m-d H:i:s');

    for ($i = 1; $i < count($rows); $i++) {
        while (count($rows[$i]) < count($header)) {
            $rows[$i][] = '';
        }

        if (($rows[$i][$sessionIdx] ?? '') === $sessionId) {
            if (($rows[$i][$stockUpdatedIdx] ?? '') === '1') {
                return;
            }

            $rows[$i][$stockUpdatedIdx] = '1';
            $rows[$i][$stockUpdatedAtIdx] = $now;
            $rows[$i][$webhookProcessedIdx] = $now;
            break;
        }
    }

    $fh = fopen($path, 'wb');
    if (!$fh) {
        return;
    }

    foreach ($rows as $row) {
        while (count($row) < count($header)) {
            $row[] = '';
        }
        fputcsv($fh, $row, ';');
    }

    fclose($fh);
}

function kapvoe_format_money($value): string
{
    return number_format((float)$value, 2, ',', '.') . ' EUR';
}

function kapvoe_mail_html_escape(?string $value): string
{
    return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}

function kapvoe_mail_base_url(array $config): string
{
    $successUrl = (string)($config['success_url'] ?? '');
    if ($successUrl === '') {
        return '';
    }

    $parts = parse_url($successUrl);
    if (!is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
        return '';
    }

    $base = $parts['scheme'] . '://' . $parts['host'];
    if (!empty($parts['port'])) {
        $base .= ':' . $parts['port'];
    }

    return $base;
}

function kapvoe_build_order_email_html(array $config, array $order, string $sessionId, bool $forAdmin): string
{
    $brand = (string)($config['mail_from_name'] ?? 'Blood Bros Sports');
    $productCode = kapvoe_mail_html_escape((string)($order['product_code'] ?? ''));
    $customerName = kapvoe_mail_html_escape((string)($order['customer_name'] ?? ''));
    $customerEmail = kapvoe_mail_html_escape((string)($order['email'] ?? ''));
    $phone = kapvoe_mail_html_escape((string)($order['phone'] ?? ''));
    $subtotal = kapvoe_format_money($order['subtotal'] ?? $order['unit_price'] ?? '0');
    $shippingCost = kapvoe_format_money($order['shipping_cost'] ?? '0');
    $totalAmount = kapvoe_format_money($order['total_amount'] ?? $order['unit_price'] ?? '0');
    $shippingMethod = (string)($order['shipping_method'] ?? 'pickup');
    $shippingLabel = $shippingMethod === 'shipping' ? 'Enviament' : 'Entrega en persona';
    $sessionIdSafe = kapvoe_mail_html_escape($sessionId);
    $imageUrl = trim((string)($order['product_image_url'] ?? ''));
    $successLink = (string)($config['success_url'] ?? '');
    if ($successLink !== '' && $sessionId !== '') {
        $successLink .= '?session_id=' . rawurlencode($sessionId);
    }

    $deliveryBlock = $shippingMethod === 'shipping'
        ? '<div style="padding:10px 0;border-bottom:1px solid rgba(255,255,255,.08);">'
            . '<div style="font-size:12px;color:#9fb0ca;text-transform:uppercase;letter-spacing:.12em;font-weight:700;">Adreca d\'enviament</div>'
            . '<div style="margin-top:6px;font-size:15px;line-height:1.55;color:#eef4ff;">'
            . kapvoe_mail_html_escape((string)($order['address'] ?? '')) . '<br>'
            . kapvoe_mail_html_escape((string)($order['postal_code'] ?? '')) . ' '
            . kapvoe_mail_html_escape((string)($order['city'] ?? '')) . '<br>'
            . kapvoe_mail_html_escape((string)($order['province'] ?? ''))
            . '</div></div>'
        : '<div style="padding:10px 0;border-bottom:1px solid rgba(255,255,255,.08);">'
            . '<div style="font-size:12px;color:#9fb0ca;text-transform:uppercase;letter-spacing:.12em;font-weight:700;">Entrega</div>'
            . '<div style="margin-top:6px;font-size:15px;line-height:1.55;color:#eef4ff;">Quedarem amb tu per WhatsApp per entregar-te les ulleres en persona a Lleida o Mollerussa.</div>'
            . '</div>';

    $intro = $forAdmin
        ? 'S\'ha confirmat una nova compra al web. Tens el resum complet de la comanda a continuacio.'
        : 'Hem rebut la teva compra. Aqui tens un resum de la comanda confirmada.';
    $badge = $forAdmin ? 'Nova comanda cobrada' : 'Compra confirmada';
    $cta = $successLink !== ''
        ? '<a href="' . kapvoe_mail_html_escape($successLink) . '" style="display:inline-block;padding:14px 22px;border-radius:999px;background:linear-gradient(180deg, rgba(93,154,255,.9), rgba(52,115,214,.9));color:#ffffff;text-decoration:none;font-weight:800;">Veure resum</a>'
        : '';

    return '<!DOCTYPE html>
<html lang="ca">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>' . kapvoe_mail_html_escape($brand) . '</title>
</head>
<body style="margin:0;padding:24px;background:#081221;font-family:Arial,Helvetica,sans-serif;color:#f4f7fb;">
  <div style="max-width:960px;margin:0 auto;border-radius:32px;border:1px solid rgba(255,255,255,.10);background:linear-gradient(180deg,#172544 0%,#0f1b32 100%);box-shadow:0 16px 44px rgba(0,0,0,.34);overflow:hidden;">
    <div style="padding:28px;">
      <div style="display:flex;align-items:center;gap:18px;">
        <div style="width:84px;height:84px;border-radius:24px;background:linear-gradient(135deg, rgba(255,255,255,.08), rgba(255,255,255,.03));border:1px solid rgba(255,255,255,.10);display:flex;align-items:center;justify-content:center;overflow:hidden;">
          <img src="' . kapvoe_mail_html_escape(kapvoe_mail_base_url($config) . '/assets/logo/bloodbros-sports-logo.png') . '" alt="' . kapvoe_mail_html_escape($brand) . '" style="max-width:82%;max-height:82%;display:block;">
        </div>
        <div>
          <div style="margin:0 0 6px;font-size:24px;line-height:1.2;font-weight:800;color:#ffffff;">Pagament correcte</div>
          <div style="margin:0;font-size:16px;line-height:1.5;color:#b9c8df;">' . kapvoe_mail_html_escape($intro) . '</div>
        </div>
      </div>

      <div style="margin:22px 0 22px;display:inline-block;padding:10px 16px;border-radius:999px;background:rgba(40,210,103,.14);border:1px solid rgba(40,210,103,.28);color:#c8ffd9;font-weight:800;">' . kapvoe_mail_html_escape($badge) . '</div>

      <div style="display:grid;gap:18px;">
        <div style="display:grid;grid-template-columns:120px 1fr;gap:18px;align-items:center;border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);border-radius:24px;padding:18px;">
          <div style="width:120px;height:120px;border-radius:18px;background:rgba(255,255,255,.92);display:flex;align-items:center;justify-content:center;overflow:hidden;">'
            . ($imageUrl !== ''
                ? '<img src="' . kapvoe_mail_html_escape($imageUrl) . '" alt="' . $productCode . '" style="width:100%;height:100%;object-fit:contain;display:block;">'
                : '<div style="padding:8px;color:#0b1630;font-size:12px;font-weight:800;text-align:center;">Sense imatge</div>')
          . '</div>
          <div>
            <div style="margin:0 0 8px;font-size:38px;line-height:1;font-weight:900;letter-spacing:-.04em;color:#ffffff;">' . $productCode . '</div>
            <div style="font-size:15px;line-height:1.55;color:#b9c8df;">
              Sessio Stripe: <strong style="color:#eef4ff;">' . $sessionIdSafe . '</strong><br>
              Metode: <strong style="color:#eef4ff;">' . kapvoe_mail_html_escape($shippingLabel) . '</strong>
            </div>
          </div>
        </div>

        <div style="display:grid;grid-template-columns:1fr 1fr;gap:18px;">
          <div style="border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);border-radius:24px;padding:18px;">
            <div style="margin:0 0 14px;color:#9fb0ca;font-size:12px;text-transform:uppercase;letter-spacing:.16em;font-weight:800;">Resum economic</div>
            <div style="display:flex;justify-content:space-between;gap:14px;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.06);font-size:15px;color:#eef4ff;"><span>Subtotal producte</span><strong>' . kapvoe_mail_html_escape($subtotal) . '</strong></div>
            <div style="display:flex;justify-content:space-between;gap:14px;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.06);font-size:15px;color:#eef4ff;"><span>Enviament</span><strong>' . kapvoe_mail_html_escape($shippingCost) . '</strong></div>
            <div style="display:flex;justify-content:space-between;gap:14px;padding:12px 0 0;font-size:18px;font-weight:800;color:#ffffff;"><span>Total</span><strong>' . kapvoe_mail_html_escape($totalAmount) . '</strong></div>
          </div>

          <div style="border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);border-radius:24px;padding:18px;">
            <div style="margin:0 0 14px;color:#9fb0ca;font-size:12px;text-transform:uppercase;letter-spacing:.16em;font-weight:800;">Dades de contacte</div>
            <div style="padding:10px 0;border-bottom:1px solid rgba(255,255,255,.06);"><div style="font-size:12px;color:#9fb0ca;text-transform:uppercase;letter-spacing:.12em;font-weight:700;">Nom</div><div style="margin-top:6px;font-size:15px;line-height:1.55;color:#eef4ff;">' . $customerName . '</div></div>
            <div style="padding:10px 0;border-bottom:1px solid rgba(255,255,255,.06);"><div style="font-size:12px;color:#9fb0ca;text-transform:uppercase;letter-spacing:.12em;font-weight:700;">Telefon</div><div style="margin-top:6px;font-size:15px;line-height:1.55;color:#eef4ff;">' . $phone . '</div></div>
            <div style="padding:10px 0;border-bottom:1px solid rgba(255,255,255,.06);"><div style="font-size:12px;color:#9fb0ca;text-transform:uppercase;letter-spacing:.12em;font-weight:700;">Correu electronic</div><div style="margin-top:6px;font-size:15px;line-height:1.55;color:#eef4ff;">' . $customerEmail . '</div></div>
            ' . $deliveryBlock . '
          </div>
        </div>

        ' . ($cta !== '' ? '<div style="padding-top:6px;">' . $cta . '</div>' : '') . '
      </div>
    </div>
  </div>
</body>
</html>';
}

function kapvoe_mail_log_path(array $config): string
{
    $dir = rtrim((string)($config['orders_storage_dir'] ?? (__DIR__ . '/data')), DIRECTORY_SEPARATOR);
    if (!is_dir($dir)) {
        @mkdir($dir, 0775, true);
    }

    return $dir . DIRECTORY_SEPARATOR . 'mail.log';
}

function kapvoe_log_mail_event(array $config, string $message): void
{
    $line = '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL;
    $path = kapvoe_mail_log_path($config);
    $written = @file_put_contents($path, $line, FILE_APPEND);

    if ($written === false) {
        $fallbackPath = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'kapvoe-mail.log';
        @file_put_contents($fallbackPath, $line, FILE_APPEND);
        error_log('[KAPVOE MAIL] ' . trim($message) . ' | primary_log=' . $path . ' | fallback_log=' . $fallbackPath);
        return;
    }

    error_log('[KAPVOE MAIL] ' . trim($message) . ' | log=' . $path);
}

function kapvoe_analytics_log_path(array $config): string
{
    $dir = rtrim((string)($config['orders_storage_dir'] ?? (__DIR__ . '/data')), DIRECTORY_SEPARATOR);
    if (!is_dir($dir)) {
        @mkdir($dir, 0775, true);
    }

    return $dir . DIRECTORY_SEPARATOR . 'analytics.log';
}

function kapvoe_log_analytics_event(array $config, string $message): void
{
    $line = '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL;
    $path = kapvoe_analytics_log_path($config);
    @file_put_contents($path, $line, FILE_APPEND);
    error_log('[KAPVOE ANALYTICS] ' . trim($message) . ' | log=' . $path);
}

function kapvoe_internal_notification_log_path(array $config): string
{
    $configuredPath = trim((string)($config['internal_notification_log_path'] ?? ''));
    if ($configuredPath !== '') {
        return $configuredPath;
    }

    $dir = rtrim((string)($config['orders_storage_dir'] ?? (__DIR__ . '/data')), DIRECTORY_SEPARATOR);
    if (!is_dir($dir)) {
        @mkdir($dir, 0775, true);
    }

    return $dir . DIRECTORY_SEPARATOR . 'internal-notifications.log';
}

function kapvoe_log_internal_notification(array $config, array $payload): bool
{
    $path = kapvoe_internal_notification_log_path($config);
    $line = '[' . date('Y-m-d H:i:s') . '] ' . json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
    $written = @file_put_contents($path, $line, FILE_APPEND);

    if ($written === false) {
        error_log('[KAPVOE INTERNAL] No s\'ha pogut escriure el log intern: ' . $path);
        return false;
    }

    error_log('[KAPVOE INTERNAL] log=' . $path);
    return true;
}

function kapvoe_build_internal_notification_payload(array $order, string $sessionId): array
{
    return [
        'event' => 'order_paid',
        'session_id' => $sessionId,
        'order_id' => (string)($order['order_id'] ?? ''),
        'product_code' => (string)($order['product_code'] ?? ''),
        'product_name' => (string)($order['product_name'] ?? ''),
        'customer_name' => (string)($order['customer_name'] ?? ''),
        'customer_email' => (string)($order['email'] ?? ''),
        'phone' => (string)($order['phone'] ?? ''),
        'shipping_method' => (string)($order['shipping_method'] ?? ''),
        'total_amount' => (string)($order['total_amount'] ?? ''),
        'created_at' => (string)($order['created_at'] ?? ''),
        'sent_at' => date('c'),
    ];
}

function kapvoe_send_internal_notification(array $config, array $order, string $sessionId): array
{
    $enabled = (bool)($config['internal_notification_enabled'] ?? true);
    if (!$enabled) {
        return [
            'enabled' => false,
            'sent' => false,
            'channel' => 'disabled',
        ];
    }

    $payload = kapvoe_build_internal_notification_payload($order, $sessionId);
    $webhookUrl = trim((string)($config['internal_notification_webhook_url'] ?? ''));
    $token = trim((string)($config['internal_notification_webhook_token'] ?? ''));
    $timeout = max(5, (int)($config['internal_notification_timeout'] ?? 10));

    if ($webhookUrl === '') {
        $logged = kapvoe_log_internal_notification($config, $payload);

        return [
            'enabled' => true,
            'sent' => $logged,
            'channel' => 'log',
        ];
    }

    try {
        $headers = [];
        if ($token !== '') {
            $headers[] = 'Authorization: Bearer ' . $token;
        }

        $result = kapvoe_post_json($webhookUrl, $headers, $payload, $timeout);
        $statusCode = (int)($result['status_code'] ?? 0);

        if ($statusCode < 200 || $statusCode >= 300) {
            throw new RuntimeException('Webhook intern ha respost amb HTTP ' . $statusCode);
        }

        kapvoe_log_mail_event($config, 'INTERNAL webhook OK session=' . $sessionId . ' url=' . $webhookUrl);

        return [
            'enabled' => true,
            'sent' => true,
            'channel' => 'webhook',
        ];
    } catch (Throwable $e) {
        kapvoe_log_mail_event($config, 'INTERNAL webhook FAIL session=' . $sessionId . ' error=' . $e->getMessage());
        $logged = kapvoe_log_internal_notification($config, $payload + ['webhook_error' => $e->getMessage()]);

        return [
            'enabled' => true,
            'sent' => $logged,
            'channel' => $logged ? 'log-fallback' : 'failed',
            'error' => $e->getMessage(),
        ];
    }
}

function kapvoe_encode_email_header(string $value): string
{
    if ($value === '' || !preg_match('/[^\x20-\x7E]/', $value)) {
        return $value;
    }

    return '=?UTF-8?B?' . base64_encode($value) . '?=';
}

function kapvoe_normalize_email_text(string $htmlBody): string
{
    $text = str_replace(['<br>', '<br/>', '<br />'], "\n", $htmlBody);
    $text = html_entity_decode(strip_tags($text), ENT_QUOTES | ENT_HTML5, 'UTF-8');
    $text = preg_replace("/\r\n|\r/u", "\n", $text) ?? $text;
    $text = preg_replace("/\n{3,}/u", "\n\n", $text) ?? $text;

    return trim($text);
}

function kapvoe_build_email_message(
    string $fromEmail,
    string $fromName,
    string $replyTo,
    string $toEmail,
    string $subject,
    string $htmlBody
): array {
    $plainText = kapvoe_normalize_email_text($htmlBody);
    $boundary = 'kapvoe_' . bin2hex(random_bytes(12));
    $encodedSubject = kapvoe_encode_email_header($subject);
    $encodedFromName = kapvoe_encode_email_header($fromName);

    $headers = [
        'MIME-Version: 1.0',
        'From: ' . $encodedFromName . ' <' . $fromEmail . '>',
        'Reply-To: ' . $replyTo,
        'To: <' . $toEmail . '>',
        'Subject: ' . $encodedSubject,
        'Date: ' . date(DATE_RFC2822),
        'Message-ID: <' . bin2hex(random_bytes(16)) . '@' . ($_SERVER['SERVER_NAME'] ?? 'localhost') . '>',
        'Content-Type: multipart/alternative; boundary="' . $boundary . '"',
    ];

    $message =
        "--{$boundary}\r\n" .
        "Content-Type: text/plain; charset=UTF-8\r\n" .
        "Content-Transfer-Encoding: 8bit\r\n\r\n" .
        $plainText . "\r\n\r\n" .
        "--{$boundary}\r\n" .
        "Content-Type: text/html; charset=UTF-8\r\n" .
        "Content-Transfer-Encoding: 8bit\r\n\r\n" .
        $htmlBody . "\r\n\r\n" .
        "--{$boundary}--\r\n";

    return [
        'headers' => $headers,
        'message' => $message,
        'subject' => $encodedSubject,
    ];
}

function kapvoe_post_json(string $url, array $headers, array $payload, int $timeout = 15): array
{
    if (!function_exists('curl_init')) {
        throw new RuntimeException('cURL no esta disponible al PHP');
    }

    $ch = curl_init($url);
    if ($ch === false) {
        throw new RuntimeException('No s\'ha pogut inicialitzar cURL');
    }

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        CURLOPT_HTTPHEADER => array_merge([
            'Content-Type: application/json',
            'Accept: application/json',
        ], $headers),
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_TIMEOUT => $timeout,
        CURLOPT_CONNECTTIMEOUT => min(10, $timeout),
    ]);

    $response = curl_exec($ch);
    if ($response === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException('Error cURL: ' . $error);
    }

    $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $decoded = json_decode($response, true);

    return [
        'status_code' => $statusCode,
        'body' => $response,
        'json' => is_array($decoded) ? $decoded : null,
    ];
}

function kapvoe_analytics_enabled(array $config): bool
{
    return (bool)($config['analytics_enabled'] ?? false)
        && trim((string)($config['analytics_sync_url'] ?? '')) !== '';
}

function kapvoe_send_analytics_payload(array $config, array $payload): bool
{
    if (!kapvoe_analytics_enabled($config)) {
        return false;
    }

    $url = trim((string)($config['analytics_sync_url'] ?? ''));
    $token = trim((string)($config['analytics_sync_token'] ?? ''));
    $timeout = max(5, (int)($config['analytics_timeout'] ?? 10));
    $headers = [];
    $requestPayload = $payload;

    if ($token !== '') {
        $headers[] = 'Authorization: Bearer ' . $token;
        $requestPayload['token'] = $token;
    }

    $result = kapvoe_post_json($url, $headers, $requestPayload, $timeout);
    $statusCode = (int)($result['status_code'] ?? 0);
    if ($statusCode < 200 || $statusCode >= 300) {
        throw new RuntimeException('Analytics HTTP ' . $statusCode);
    }

    return true;
}

function kapvoe_track_analytics_event(array $config, array $payload): bool
{
    if (!kapvoe_analytics_enabled($config)) {
        return false;
    }

    try {
        $sent = kapvoe_send_analytics_payload($config, $payload);
        kapvoe_log_analytics_event(
            $config,
            'event=' . (string)($payload['event_type'] ?? 'unknown')
            . ' session=' . (string)($payload['session_id'] ?? '')
            . ' order=' . (string)($payload['order_id'] ?? '')
            . ' sent=' . ($sent ? '1' : '0')
        );
        return $sent;
    } catch (Throwable $e) {
        kapvoe_log_analytics_event(
            $config,
            'ERROR event=' . (string)($payload['event_type'] ?? 'unknown')
            . ' session=' . (string)($payload['session_id'] ?? '')
            . ' order=' . (string)($payload['order_id'] ?? '')
            . ' ' . $e->getMessage()
        );
        return false;
    }
}

function kapvoe_build_payment_success_event(array $order, string $sessionId): array
{
    return [
        'timestamp' => date('c'),
        'event_type' => 'payment_success',
        'product_code' => (string)($order['product_code'] ?? ''),
        'model' => (string)($order['product_name'] ?? ''),
        'category' => (string)($order['analytics_page_type'] ?? ''),
        'price' => (string)($order['unit_price'] ?? ''),
        'stock' => '',
        'page_url' => (string)($order['analytics_page_url'] ?? ''),
        'user_agent' => (string)($order['analytics_user_agent'] ?? ''),
        'session_id' => (string)($order['analytics_session_id'] ?? ''),
        'event_id' => 'evt_' . bin2hex(random_bytes(8)),
        'order_id' => (string)($order['order_id'] ?? ''),
        'stripe_session_id' => $sessionId,
        'payment_status' => (string)($order['payment_status'] ?? 'paid'),
        'quantity' => (string)($order['quantity'] ?? '1'),
        'shipping_method' => (string)($order['shipping_method'] ?? ''),
        'total_amount' => (string)($order['total_amount'] ?? ''),
        'referrer' => (string)($order['analytics_referrer'] ?? ''),
        'utm_source' => (string)($order['analytics_utm_source'] ?? ''),
        'utm_medium' => (string)($order['analytics_utm_medium'] ?? ''),
        'utm_campaign' => (string)($order['analytics_utm_campaign'] ?? ''),
        'device_type' => (string)($order['analytics_device_type'] ?? ''),
        'page_type' => (string)($order['analytics_page_type'] ?? ''),
    ];
}

function kapvoe_send_via_resend(
    array $config,
    string $fromEmail,
    string $fromName,
    string $replyTo,
    string $toEmail,
    string $subject,
    string $htmlBody
): bool {
    $apiKey = trim((string)($config['resend_api_key'] ?? ''));
    $apiUrl = trim((string)($config['resend_api_url'] ?? 'https://api.resend.com/emails'));
    $timeout = max(5, (int)($config['smtp_timeout'] ?? 15));

    if ($apiKey === '') {
        throw new RuntimeException('Falta configurar resend_api_key');
    }

    $payload = [
        'from' => trim($fromName) !== '' ? ($fromName . ' <' . $fromEmail . '>') : $fromEmail,
        'to' => [$toEmail],
        'subject' => $subject,
        'html' => $htmlBody,
        'text' => kapvoe_normalize_email_text($htmlBody),
        'reply_to' => [$replyTo],
    ];

    $result = kapvoe_post_json($apiUrl, [
        'Authorization: Bearer ' . $apiKey,
    ], $payload, $timeout);

    if (($result['status_code'] ?? 0) < 200 || ($result['status_code'] ?? 0) >= 300) {
        $json = $result['json'] ?? [];
        $message = '';

        if (is_array($json)) {
            $message = (string)($json['message'] ?? $json['error'] ?? '');
        }
        if ($message === '') {
            $message = (string)($result['body'] ?? 'Resposta invalida de Resend');
        }

        throw new RuntimeException('Resend API error: ' . $message);
    }

    return true;
}

function kapvoe_smtp_expect($socket, array $expectedCodes, string $context): string
{
    $response = '';

    while (!feof($socket)) {
        $line = fgets($socket, 515);
        if ($line === false) {
            break;
        }

        $response .= $line;

        if (preg_match('/^\d{3} /', $line) === 1) {
            break;
        }
    }

    if ($response === '') {
        throw new RuntimeException('SMTP sense resposta en ' . $context);
    }

    $code = (int)substr($response, 0, 3);
    if (!in_array($code, $expectedCodes, true)) {
        throw new RuntimeException('SMTP error en ' . $context . ': ' . trim($response));
    }

    return $response;
}

function kapvoe_smtp_write($socket, string $command): void
{
    $written = fwrite($socket, $command);
    if ($written === false || $written < strlen($command)) {
        throw new RuntimeException('No s\'ha pogut escriure al servidor SMTP');
    }
}

function kapvoe_send_via_smtp_curl(
    array $config,
    string $fromEmail,
    string $fromName,
    string $replyTo,
    string $toEmail,
    string $subject,
    string $htmlBody
): bool {
    if (!function_exists('curl_init')) {
        throw new RuntimeException('cURL no esta disponible al PHP');
    }

    $host = trim((string)($config['smtp_host'] ?? ''));
    if ($host === '') {
        return false;
    }

    $port = (int)($config['smtp_port'] ?? 587);
    $username = trim((string)($config['smtp_username'] ?? ''));
    $password = (string)($config['smtp_password'] ?? '');
    $encryption = strtolower(trim((string)($config['smtp_encryption'] ?? 'tls')));
    $timeout = max(5, (int)($config['smtp_timeout'] ?? 15));
    $allowInvalidCert = (bool)($config['smtp_allow_invalid_certificates'] ?? false);
    $built = kapvoe_build_email_message($fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
    $payload = implode("\r\n", $built['headers']) . "\r\n\r\n" . $built['message'];
    $urlScheme = $encryption === 'ssl' ? 'smtps' : 'smtp';
    $url = $urlScheme . '://' . $host . ':' . $port;
    $payloadLength = strlen($payload);
    $offset = 0;

    $ch = curl_init($url);
    if ($ch === false) {
        throw new RuntimeException('No s\'ha pogut inicialitzar cURL SMTP');
    }

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_UPLOAD, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, min(10, $timeout));
    curl_setopt($ch, CURLOPT_MAIL_FROM, '<' . $fromEmail . '>');
    curl_setopt($ch, CURLOPT_MAIL_RCPT, ['<' . $toEmail . '>']);
    curl_setopt($ch, CURLOPT_INFILESIZE, $payloadLength);
    curl_setopt($ch, CURLOPT_READFUNCTION, static function ($curl, $fd, $length) use (&$payload, &$offset, $payloadLength) {
        if ($offset >= $payloadLength) {
            return '';
        }

        $chunk = substr($payload, $offset, $length);
        $offset += strlen($chunk);
        return $chunk;
    });

    if ($username !== '') {
        curl_setopt($ch, CURLOPT_USERNAME, $username);
        curl_setopt($ch, CURLOPT_PASSWORD, $password);
    }

    if ($encryption === 'tls') {
        curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_ALL);
    } elseif ($encryption === '' || $encryption === 'none') {
        curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_NONE);
    } else {
        curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_ALL);
    }

    if ($allowInvalidCert) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    }

    $response = curl_exec($ch);
    if ($response === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException('SMTP cURL error: ' . $error);
    }

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

    if ($statusCode >= 400) {
        throw new RuntimeException('SMTP cURL ha respost amb codi ' . $statusCode);
    }

    return true;
}

function kapvoe_send_via_smtp(
    array $config,
    string $fromEmail,
    string $fromName,
    string $replyTo,
    string $toEmail,
    string $subject,
    string $htmlBody
): bool {
    try {
        return kapvoe_send_via_smtp_curl($config, $fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
    } catch (Throwable $curlError) {
        kapvoe_log_mail_event($config, 'SMTP cURL FAIL -> ' . $toEmail . ' [' . $subject . '] ' . $curlError->getMessage());
    }

    $host = trim((string)($config['smtp_host'] ?? ''));
    if ($host === '') {
        return false;
    }

    $port = (int)($config['smtp_port'] ?? 587);
    $username = trim((string)($config['smtp_username'] ?? ''));
    $password = (string)($config['smtp_password'] ?? '');
    $encryption = strtolower(trim((string)($config['smtp_encryption'] ?? 'tls')));
    $timeout = max(5, (int)($config['smtp_timeout'] ?? 15));
    $allowInvalidCert = (bool)($config['smtp_allow_invalid_certificates'] ?? false);
    $secureTransport = $encryption === 'ssl' ? 'ssl://' : '';
    $remote = $secureTransport . $host . ':' . $port;
    $errno = 0;
    $errstr = '';
    $contextOptions = [];

    if ($allowInvalidCert) {
        $contextOptions['ssl'] = [
            'verify_peer' => false,
            'verify_peer_name' => false,
            'allow_self_signed' => true,
            'SNI_enabled' => true,
            'peer_name' => $host,
        ];
    }

    $context = !empty($contextOptions) ? stream_context_create($contextOptions) : null;

    $socket = @stream_socket_client(
        $remote,
        $errno,
        $errstr,
        $timeout,
        STREAM_CLIENT_CONNECT,
        $context
    );
    if (!$socket) {
        throw new RuntimeException('No s\'ha pogut connectar a SMTP ' . $host . ':' . $port . ' (' . $errstr . ')');
    }

    stream_set_timeout($socket, $timeout);

    try {
        kapvoe_smtp_expect($socket, [220], 'connexio');

        $clientHost = $_SERVER['SERVER_NAME'] ?? 'localhost';
        kapvoe_smtp_write($socket, 'EHLO ' . $clientHost . "\r\n");
        kapvoe_smtp_expect($socket, [250], 'EHLO');

        if ($encryption === 'tls') {
            kapvoe_smtp_write($socket, "STARTTLS\r\n");
            kapvoe_smtp_expect($socket, [220], 'STARTTLS');

            $cryptoMethod = defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')
                ? STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
                : STREAM_CRYPTO_METHOD_TLS_CLIENT;
            $cryptoEnabled = @stream_socket_enable_crypto($socket, true, $cryptoMethod);
            if ($cryptoEnabled !== true) {
                throw new RuntimeException('No s\'ha pogut establir el canal TLS amb SMTP');
            }

            kapvoe_smtp_write($socket, 'EHLO ' . $clientHost . "\r\n");
            kapvoe_smtp_expect($socket, [250], 'EHLO post-TLS');
        }

        if ($username !== '') {
            kapvoe_smtp_write($socket, "AUTH LOGIN\r\n");
            kapvoe_smtp_expect($socket, [334], 'AUTH LOGIN');
            kapvoe_smtp_write($socket, base64_encode($username) . "\r\n");
            kapvoe_smtp_expect($socket, [334], 'SMTP username');
            kapvoe_smtp_write($socket, base64_encode($password) . "\r\n");
            kapvoe_smtp_expect($socket, [235], 'SMTP password');
        }

        $built = kapvoe_build_email_message($fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
        $smtpPayload = implode("\r\n", $built['headers']) . "\r\n\r\n" . $built['message'];
        $smtpPayload = preg_replace("/(?m)^\./", '..', $smtpPayload) ?? $smtpPayload;

        kapvoe_smtp_write($socket, 'MAIL FROM:<' . $fromEmail . ">\r\n");
        kapvoe_smtp_expect($socket, [250], 'MAIL FROM');
        kapvoe_smtp_write($socket, 'RCPT TO:<' . $toEmail . ">\r\n");
        kapvoe_smtp_expect($socket, [250, 251], 'RCPT TO');
        kapvoe_smtp_write($socket, "DATA\r\n");
        kapvoe_smtp_expect($socket, [354], 'DATA');
        kapvoe_smtp_write($socket, $smtpPayload . "\r\n.\r\n");
        kapvoe_smtp_expect($socket, [250], 'envio DATA');
        kapvoe_smtp_write($socket, "QUIT\r\n");

        return true;
    } finally {
        fclose($socket);
    }
}

function kapvoe_send_html_email(array $config, string $toEmail, string $subject, string $htmlBody): bool
{
    $toEmail = trim($toEmail);
    if ($toEmail === '') {
        return false;
    }

    $fromEmail = trim((string)($config['mail_from_email'] ?? ''));
    $fromName = trim((string)($config['mail_from_name'] ?? 'Blood Bros Sports'));
    $replyTo = trim((string)($config['mail_reply_to'] ?? $fromEmail));

    if ($fromEmail === '') {
        throw new RuntimeException('Falta configurar mail_from_email');
    }

    $mailTransport = strtolower(trim((string)($config['mail_transport'] ?? 'auto')));
    $smtpConfigured = (bool)($config['smtp_enabled'] ?? false) || trim((string)($config['smtp_host'] ?? '')) !== '';
    $resendConfigured = trim((string)($config['resend_api_key'] ?? '')) !== '';

    try {
        $built = kapvoe_build_email_message($fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);

        if ($mailTransport === 'resend') {
            $resendSent = kapvoe_send_via_resend($config, $fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
            kapvoe_log_mail_event($config, 'Resend OK -> ' . $toEmail . ' [' . $subject . ']');
            return $resendSent;
        }

        if ($mailTransport === 'smtp') {
            $smtpSent = kapvoe_send_via_smtp($config, $fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
            kapvoe_log_mail_event($config, 'SMTP OK -> ' . $toEmail . ' [' . $subject . ']');
            return $smtpSent;
        }

        if ($mailTransport === 'auto' && $resendConfigured) {
            $resendSent = kapvoe_send_via_resend($config, $fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
            kapvoe_log_mail_event($config, 'Resend auto OK -> ' . $toEmail . ' [' . $subject . ']');
            return $resendSent;
        }

        $mailSent = mail($toEmail, $built['subject'], $built['message'], implode("\r\n", $built['headers']));
        if ($mailSent) {
            kapvoe_log_mail_event($config, 'mail() OK -> ' . $toEmail . ' [' . $subject . ']');
            return true;
        }

        $lastPhpError = error_get_last();
        $lastPhpErrorMessage = is_array($lastPhpError) ? (string)($lastPhpError['message'] ?? '') : '';
        kapvoe_log_mail_event(
            $config,
            'mail() FAIL -> ' . $toEmail . ' [' . $subject . ']'
            . ($lastPhpErrorMessage !== '' ? ' ' . $lastPhpErrorMessage : '')
        );

        if ($smtpConfigured && ($mailTransport === 'mail' || $mailTransport === 'auto')) {
            $smtpSent = kapvoe_send_via_smtp($config, $fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
            kapvoe_log_mail_event($config, 'SMTP fallback OK -> ' . $toEmail . ' [' . $subject . ']');
            return $smtpSent;
        }

        if ($resendConfigured && ($mailTransport === 'mail' || $mailTransport === 'auto')) {
            $resendSent = kapvoe_send_via_resend($config, $fromEmail, $fromName, $replyTo, $toEmail, $subject, $htmlBody);
            kapvoe_log_mail_event($config, 'Resend fallback OK -> ' . $toEmail . ' [' . $subject . ']');
            return $resendSent;
        }

        return false;
    } catch (Throwable $e) {
        kapvoe_log_mail_event($config, 'ERROR -> ' . $toEmail . ' [' . $subject . '] ' . $e->getMessage());
        return false;
    }
}

function kapvoe_mark_order_email_notifications(array $config, string $sessionId, bool $customerSent, bool $adminSent): void
{
    $path = kapvoe_orders_csv_path($config);
    kapvoe_upgrade_orders_csv_schema($config);
    if (!file_exists($path)) {
        return;
    }

    $rows = [];
    $fh = fopen($path, 'rb');
    if (!$fh) {
        return;
    }

    while (($row = fgetcsv($fh, 0, ';')) !== false) {
        $rows[] = $row;
    }
    fclose($fh);

    if (count($rows) < 2) {
        return;
    }

    $header = $rows[0];
    $sessionIdx = array_search('stripe_session_id', $header, true);
    $customerIdx = array_search('customer_email_sent', $header, true);
    $adminIdx = array_search('admin_email_sent', $header, true);
    $sentAtIdx = array_search('email_notifications_sent_at', $header, true);

    if ($sessionIdx === false || $customerIdx === false || $adminIdx === false || $sentAtIdx === false) {
        return;
    }

    $now = date('Y-m-d H:i:s');

    for ($i = 1; $i < count($rows); $i++) {
        while (count($rows[$i]) < count($header)) {
            $rows[$i][] = '';
        }

        if (($rows[$i][$sessionIdx] ?? '') === $sessionId) {
            if ($customerSent) {
                $rows[$i][$customerIdx] = '1';
            }
            if ($adminSent) {
                $rows[$i][$adminIdx] = '1';
            }
            if ($customerSent || $adminSent) {
                $rows[$i][$sentAtIdx] = $now;
            }
            break;
        }
    }

    $fh = fopen($path, 'wb');
    if (!$fh) {
        return;
    }

    foreach ($rows as $row) {
        while (count($row) < count($header)) {
            $row[] = '';
        }
        fputcsv($fh, $row, ';');
    }
    fclose($fh);
}

function kapvoe_send_order_notifications(array $config, array $order, string $sessionId): array
{
    $customerAlreadySent = (string)($order['customer_email_sent'] ?? '') === '1';
    $adminAlreadySent = (string)($order['admin_email_sent'] ?? '') === '1';

    $customerSent = false;
    $adminSent = false;
    $internalResult = [
        'enabled' => false,
        'sent' => false,
        'channel' => 'disabled',
    ];
    $customerEmail = trim((string)($order['email'] ?? ''));
    $adminEmail = trim((string)($config['admin_notification_email'] ?? ''));

    kapvoe_log_mail_event(
        $config,
        'notify session=' . $sessionId
        . ' customer_already=' . ($customerAlreadySent ? '1' : '0')
        . ' admin_already=' . ($adminAlreadySent ? '1' : '0')
        . ' customer_email=' . ($customerEmail !== '' ? $customerEmail : '(empty)')
        . ' admin_email=' . ($adminEmail !== '' ? $adminEmail : '(empty)')
    );

    if (!$customerAlreadySent && $customerEmail !== '') {
        $customerSubject = 'Compra confirmada - ' . (string)($order['product_code'] ?? 'Blood Bros Sports');
        $customerHtml = kapvoe_build_order_email_html($config, $order, $sessionId, false);
        $customerSent = kapvoe_send_html_email($config, $customerEmail, $customerSubject, $customerHtml);
    }

    if (!$adminAlreadySent && $adminEmail !== '') {
        $adminSubject = 'Nova comanda - ' . (string)($order['product_code'] ?? 'Blood Bros Sports');
        $adminHtml = kapvoe_build_order_email_html($config, $order, $sessionId, true);
        $adminSent = kapvoe_send_html_email($config, $adminEmail, $adminSubject, $adminHtml);
    }

    $internalResult = kapvoe_send_internal_notification($config, $order, $sessionId);

    if ($customerSent || $adminSent) {
        kapvoe_mark_order_email_notifications($config, $sessionId, $customerSent, $adminSent);
    }

    $result = [
        'customer_already_sent' => $customerAlreadySent,
        'admin_already_sent' => $adminAlreadySent,
        'customer_sent' => $customerSent,
        'admin_sent' => $adminSent,
        'internal_notification' => $internalResult,
    ];

    kapvoe_log_mail_event(
        $config,
        'notify_result session=' . $sessionId
        . ' customer_sent=' . ($customerSent ? '1' : '0')
        . ' admin_sent=' . ($adminSent ? '1' : '0')
        . ' internal_channel=' . (string)($internalResult['channel'] ?? 'unknown')
        . ' internal_sent=' . (!empty($internalResult['sent']) ? '1' : '0')
    );

    return $result;
}

function kapvoe_create_checkout_session(array $config, array $payload): array
{
    $secretKey = (string)($config['stripe_secret_key'] ?? '');
    $currency = (string)($config['currency'] ?? 'eur');
    $successUrl = (string)($config['success_url'] ?? '');
    $cancelUrl = (string)($config['cancel_url'] ?? '');

    if ($secretKey === '') {
        throw new RuntimeException('Falta stripe_secret_key');
    }

    if ($successUrl === '' || $cancelUrl === '') {
        throw new RuntimeException('Falten success_url o cancel_url');
    }

    $lineItems = [
        [
            'name' => (string)($payload['product_name'] ?? 'Producte'),
            'unit_amount' => (int)($payload['unit_amount_cents'] ?? 0),
            'quantity' => max(1, (int)($payload['quantity'] ?? 1)),
        ],
    ];

    $shippingMethod = (string)($payload['shipping_method'] ?? 'pickup');
    $shippingCostCents = (int)($payload['shipping_cost_cents'] ?? 0);

    if ($shippingMethod === 'shipping' && $shippingCostCents > 0) {
        $lineItems[] = [
            'name' => 'Enviament',
            'unit_amount' => $shippingCostCents,
            'quantity' => 1,
        ];
    }

    $postFields = [
        'mode' => 'payment',
        'success_url' => $successUrl . '?session_id={CHECKOUT_SESSION_ID}',
        'cancel_url' => $cancelUrl,
        'customer_email' => (string)($payload['email'] ?? ''),
        'metadata[order_id]' => (string)($payload['order_id'] ?? ''),
        'metadata[product_code]' => (string)($payload['product_code'] ?? ''),
        'metadata[shipping_method]' => $shippingMethod,
    ];

    foreach ($lineItems as $i => $item) {
        $postFields["line_items[$i][price_data][currency]"] = $currency;
        $postFields["line_items[$i][price_data][product_data][name]"] = $item['name'];
        $postFields["line_items[$i][price_data][unit_amount]"] = (string)$item['unit_amount'];
        $postFields["line_items[$i][quantity]"] = (string)$item['quantity'];
    }

    $ch = curl_init('https://api.stripe.com/v1/checkout/sessions');

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($postFields),
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Content-Type: application/x-www-form-urlencoded',
        ],
        CURLOPT_TIMEOUT => 30,
        CURLOPT_CONNECTTIMEOUT => 10,
    ]);

    $response = curl_exec($ch);

    if ($response === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException('Error cURL Stripe: ' . $error);
    }

    $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $json = json_decode($response, true);

    if ($statusCode < 200 || $statusCode >= 300) {
        $message = is_array($json) ? ($json['error']['message'] ?? 'Error Stripe desconegut') : $response;
        throw new RuntimeException('Stripe error: ' . $message);
    }

    if (!is_array($json) || empty($json['url'])) {
        throw new RuntimeException('Resposta invàlida de Stripe');
    }

    return $json;
}

function kapvoe_decrement_sheet_stock(array $config, array $order, string $sessionId): array
{
    $url = (string)($config['stock_sync_url'] ?? '');
    $token = (string)($config['stock_sync_token'] ?? '');

    if ($url === '' || $token === '') {
        throw new RuntimeException('Falta configurar stock_sync_url o stock_sync_token');
    }

    $payload = [
        'action' => 'decrement_stock',
        'token' => $token,
        'product_code' => (string)($order['product_code'] ?? ''),
        'quantity' => max(1, (int)($order['quantity'] ?? 1)),
        'order_id' => (string)($order['order_id'] ?? ''),
        'session_id' => $sessionId,
    ];

    if ($payload['product_code'] === '') {
        throw new RuntimeException('La comanda no té product_code');
    }

    if ($payload['order_id'] === '') {
        throw new RuntimeException('La comanda no té order_id');
    }

    if (!function_exists('curl_init')) {
        throw new RuntimeException('cURL no està disponible al PHP');
    }

    $ch = curl_init($url);

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'Accept: application/json',
        ],
        CURLOPT_TIMEOUT => 20,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
    ]);

    $response = curl_exec($ch);

    if ($response === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException("Error cURL stock sync: {$error}");
    }

    $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($statusCode < 200 || $statusCode >= 300) {
        throw new RuntimeException("Stock sync HTTP {$statusCode}: {$response}");
    }

    $json = json_decode($response, true);

    if (!is_array($json)) {
        throw new RuntimeException('Resposta no JSON del stock sync: ' . $response);
    }

    if (!($json['ok'] ?? false)) {
        throw new RuntimeException('Stock sync error: ' . ($json['error'] ?? 'error desconegut'));
    }

    return $json;
}

function kapvoe_fetch_catalog_products(array $config): array
{
    $url = (string)($config['catalog_api_url'] ?? '');

    if ($url === '') {
        throw new RuntimeException('Falta configurar catalog_api_url');
    }

    if (!function_exists('curl_init')) {
        throw new RuntimeException('cURL no està disponible al PHP');
    }

    $ch = curl_init($url);

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPGET => true,
        CURLOPT_HTTPHEADER => [
            'Accept: application/json',
        ],
        CURLOPT_TIMEOUT => 20,
        CURLOPT_CONNECTTIMEOUT => 10,
    ]);

    $response = curl_exec($ch);

    if ($response === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException("Error cURL catàleg: {$error}");
    }

    $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($statusCode < 200 || $statusCode >= 300) {
        throw new RuntimeException("Catàleg HTTP {$statusCode}: {$response}");
    }

    $json = json_decode($response, true);

    if (!is_array($json) || !($json['ok'] ?? false) || !isset($json['products']) || !is_array($json['products'])) {
        throw new RuntimeException('Resposta invàlida del catàleg');
    }

    return $json['products'];
}

function kapvoe_get_catalog_product_by_code(array $config, string $productCode): array
{
    $productCode = trim($productCode);

    if ($productCode === '') {
        throw new RuntimeException('Falta product_code');
    }

    $products = kapvoe_fetch_catalog_products($config);

    foreach ($products as $product) {
        $code = trim((string)($product['product_code'] ?? ''));
        if ($code === $productCode) {
            return $product;
        }
    }

    throw new RuntimeException("No s'ha trobat el producte {$productCode} al catàleg");
}

function kapvoe_verify_stripe_signature(string $payload, string $header, string $secret, int $tolerance = 300): bool {
    if (!$header || !$secret) {
        return false;
    }

    $parts = explode(',', $header);
    $timestamp = null;
    $signature = null;

    foreach ($parts as $part) {
        [$k, $v] = array_pad(explode('=', trim($part), 2), 2, null);
        if ($k === 't') {
            $timestamp = $v;
        }
        if ($k === 'v1') {
            $signature = $v;
        }
    }

    if (!$timestamp || !$signature) {
        return false;
    }

    if (abs(time() - (int)$timestamp) > $tolerance) {
        return false;
    }

    $signedPayload = $timestamp . '.' . $payload;
    $expected = hash_hmac('sha256', $signedPayload, $secret);

    return hash_equals($expected, $signature);
}
