<?php
declare(strict_types=1);

/**
 * When PandaDoc completes, push the document link to the HubSpot Order tied to the Shopify order.
 *
 * Resolves HubSpot files from (first match wins):
 * 1) HUBSPOT_API_ROOT env / config (absolute path to the folder that contains hubspot-config.php)
 * 2) php/hubspot/api (deploy hubspot API inside the php tree)
 * 3) ../hubspot/api relative to php/ (monorepo: sibling folder next to php)
 *
 * Optional: also update Deal if HUBSPOT_PANDADOC_FALLBACK_TO_DEAL=1 (legacy).
 */

/** @return array{0: ?string, 1: list<string>} [resolvedDirOrNull, triedAbsolutePaths] */
function pd_hubspot_api_root_resolve(): array {
    $candidates = [];

    // 1) Explicit path (env, or define('HUBSPOT_API_ROOT','...') in php/config.php — works when auto path detection fails).
    $fromEnv = function_exists('pd_env')
        ? trim((string) pd_env('HUBSPOT_API_ROOT', ''))
        : trim((string) (getenv('HUBSPOT_API_ROOT') ?: ''));
    if ($fromEnv !== '') {
        $candidates[] = rtrim($fromEnv, '/\\');
    }

    // 2) Same document root as pandadoc_integration: …/pandadoc_integration → …/hubspot/api (matches /hubspot/api on site).
    $parentOfIntegration = dirname(__DIR__);
    $candidates[] = $parentOfIntegration . DIRECTORY_SEPARATOR . 'hubspot' . DIRECTORY_SEPARATOR . 'api';

    // 3) Bundle inside pandadoc_integration (optional deploy layout).
    $candidates[] = __DIR__ . DIRECTORY_SEPARATOR . 'hubspot' . DIRECTORY_SEPARATOR . 'api';

    // 4) Monorepo: parent of folder that contains pandadoc_integration, then sibling hubspot/api (e.g. php/ + ../hubspot/api).
    $candidates[] = $parentOfIntegration . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'hubspot' . DIRECTORY_SEPARATOR . 'api';

    $tried = [];
    foreach ($candidates as $dir) {
        if ($dir === '') {
            continue;
        }
        // Prefer realpath; on some hosts realpath() fails (open_basedir) while is_dir() still works.
        $resolved = realpath($dir);
        if ($resolved === false) {
            $resolved = is_dir($dir) ? $dir : null;
        }
        if ($resolved === null || !is_dir($resolved)) {
            $tried[] = $dir;
            continue;
        }
        $cfg = $resolved . DIRECTORY_SEPARATOR . 'hubspot-config.php';
        $api = $resolved . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'hubspot-api.php';
        if (is_file($cfg) && is_file($api)) {
            return [$resolved, $tried];
        }
        $tried[] = $resolved;
    }

    return [null, $tried];
}

function pd_hubspot_api_root_dir(): ?string {
    [$root] = pd_hubspot_api_root_resolve();
    return $root;
}

/** HubSpot Order property internal name for the recipient signing URL (HubSpot workflow email). */
function pd_hubspot_order_property_pandadoc_sign_url(): string {
    if (defined('HUBSPOT_ORDER_PROPERTY_PANDADOC_SIGN_URL')) {
        $p = trim((string) HUBSPOT_ORDER_PROPERTY_PANDADOC_SIGN_URL);
        if ($p !== '') {
            return $p;
        }
    }
    return trim((string) (function_exists('pd_env') ? pd_env('HUBSPOT_ORDER_PROPERTY_PANDADOC_SIGN_URL', 'pandadoc_sign_url') : (getenv('HUBSPOT_ORDER_PROPERTY_PANDADOC_SIGN_URL') ?: 'pandadoc_sign_url')));
}

/** HubSpot Order property internal name for the cleaned (certificate removed) PDF URL. */
function pd_hubspot_order_property_pandadoc_clean_pdf_url(): string {
    if (defined('HUBSPOT_ORDER_PROPERTY_PANDADOC_CLEAN_PDF_URL')) {
        $p = trim((string) HUBSPOT_ORDER_PROPERTY_PANDADOC_CLEAN_PDF_URL);
        if ($p !== '') {
            return $p;
        }
    }
    return trim((string) (function_exists('pd_env') ? pd_env('HUBSPOT_ORDER_PROPERTY_PANDADOC_CLEAN_PDF_URL', 'pandadoc_clean_pdf_url') : (getenv('HUBSPOT_ORDER_PROPERTY_PANDADOC_CLEAN_PDF_URL') ?: 'pandadoc_clean_pdf_url')));
}

/**
 * Upload a file to HubSpot Files API (v3).
 *
 * @return array{ok: bool, status?: int, id?: string, url?: string, error?: string, raw?: mixed}
 */
function pd_hubspot_files_upload(string $token, string $filePath, string $folderPath, string $access = 'PUBLIC_NOT_INDEXABLE', ?string $fileName = null): array {
    $token = trim($token);
    if ($token === '') return ['ok' => false, 'error' => 'hubspot_token_blank'];
    $filePath = trim($filePath);
    if ($filePath === '' || !is_file($filePath)) return ['ok' => false, 'error' => 'file_missing'];

    $folderPath = trim($folderPath);
    if ($folderPath === '') $folderPath = '/pandadoc';
    if ($folderPath[0] !== '/') $folderPath = '/' . $folderPath;

    $access = strtoupper(trim($access));
    if ($access === '') $access = 'PUBLIC_NOT_INDEXABLE';

    if ($fileName === null || trim($fileName) === '') {
        $fileName = basename($filePath);
    }

    $url = 'https://api.hubapi.com/files/v3/files';

    $options = json_encode(['access' => $access], JSON_UNESCAPED_SLASHES);
    if (!is_string($options) || $options === '') $options = '{"access":"PUBLIC_NOT_INDEXABLE"}';

    $postFields = [
        'file' => new CURLFile($filePath),
        'options' => $options,
        'folderPath' => $folderPath,
        'fileName' => $fileName,
    ];

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $token,
        ],
        CURLOPT_POSTFIELDS => $postFields,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 120,
    ]);
    $resp = curl_exec($ch);
    $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $err = curl_error($ch);
    curl_close($ch);

    if ($err !== '') {
        return ['ok' => false, 'status' => $status ?: 0, 'error' => $err];
    }

    $decoded = json_decode((string)$resp, true);
    $raw = is_array($decoded) ? $decoded : ['raw' => substr((string)$resp, 0, 4000)];

    if ($status < 200 || $status >= 300) {
        return ['ok' => false, 'status' => $status, 'error' => 'hubspot_files_upload_failed', 'raw' => $raw];
    }

    $id = is_array($decoded) ? (string)($decoded['id'] ?? '') : '';
    // HubSpot responses may include url under different keys; try a few.
    $fileUrl = '';
    if (is_array($decoded)) {
        $fileUrl = (string)($decoded['url'] ?? $decoded['hostingUrl'] ?? $decoded['cdnUrl'] ?? '');
        if ($fileUrl === '' && isset($decoded['hosting'] ) && is_array($decoded['hosting'])) {
            $fileUrl = (string)($decoded['hosting']['url'] ?? '');
        }
    }

    return ['ok' => true, 'status' => $status, 'id' => $id ?: null, 'url' => $fileUrl ?: null, 'raw' => $raw];
}

/**
 * After PandaDoc silent send + session, set Order.pandadoc_sign_url (or configured property) for HubSpot workflows.
 *
 * @return array{ok: bool, skipped?: bool, reason?: string, order_id?: string, status?: int, error?: string, sign_url?: string}
 */
function pd_hubspot_on_pandadoc_sign_url_ready(int $shopifyOrderId, string $signUrl): array {
    $signUrl = trim($signUrl);
    if ($signUrl === '') {
        return ['ok' => false, 'skipped' => true, 'reason' => 'empty_sign_url'];
    }

    $sync = strtolower(trim((string) (function_exists('pd_env') ? pd_env('HUBSPOT_SYNC_PANDADOC', '1') : (getenv('HUBSPOT_SYNC_PANDADOC') ?: '1'))));
    if (in_array($sync, ['0', 'false', 'no', 'off'], true)) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'disabled_by_env'];
    }

    [$root, $triedHubspotPaths] = pd_hubspot_api_root_resolve();
    if ($root === null) {
        if (function_exists('pd_log')) {
            pd_log('hubspot_api_folder_missing_sign_url', ['tried' => $triedHubspotPaths]);
        }
        return [
            'ok' => false,
            'skipped' => true,
            'reason' => 'hubspot_api_folder_missing',
            'hint' => 'Set HUBSPOT_API_ROOT to the folder containing hubspot-config.php.',
            'tried_paths' => $triedHubspotPaths,
        ];
    }

    $configFile = $root . '/hubspot-config.php';
    $apiFile = $root . '/includes/hubspot-api.php';
    if (!is_file($configFile) || !is_file($apiFile)) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_config_or_includes_missing'];
    }

    require_once $configFile;
    require_once $apiFile;

    if (!defined('HUBSPOT_PRIVATE_APP_TOKEN')) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_token_undefined'];
    }
    $tok = (string) HUBSPOT_PRIVATE_APP_TOKEN;
    if ($tok === '' || $tok === 'YOUR_PRIVATE_APP_TOKEN_HERE') {
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_token_not_configured'];
    }

    $prop = pd_hubspot_order_property_pandadoc_sign_url();
    if ($prop === '') {
        return ['ok' => false, 'skipped' => true, 'reason' => 'sign_url_property_blank'];
    }

    $shopifyIdStr = (string) $shopifyOrderId;
    $hubspotOrderId = hubspot_find_order_id_by_shopify_order($shopifyIdStr);
    if ($hubspotOrderId === null) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'no_hubspot_order_for_shopify_order'];
    }

    $out = hubspot_update_order_properties($hubspotOrderId, [$prop => $signUrl]);
    if ($out['ok']) {
        return ['ok' => true, 'order_id' => $hubspotOrderId, 'status' => $out['status'], 'sign_url' => $signUrl];
    }
    return [
        'ok' => false,
        'order_id' => $hubspotOrderId,
        'status' => $out['status'] ?? 0,
        'error' => $out['error'] ?? 'hubspot_order_patch_failed',
    ];
}

/**
 * @return array{ok: bool, skipped?: bool, reason?: string, order_id?: string, deal_id?: string, status?: int, error?: string}
 */
function pd_hubspot_on_pandadoc_completed(int $shopifyOrderId, string $pandadocDocumentId): array {
    if ($pandadocDocumentId === '') {
        return ['ok' => false, 'skipped' => true, 'reason' => 'empty_pandadoc_document_id'];
    }

    $sync = strtolower(trim((string)(function_exists('pd_env') ? pd_env('HUBSPOT_SYNC_PANDADOC', '1') : (getenv('HUBSPOT_SYNC_PANDADOC') ?: '1'))));
    if (in_array($sync, ['0', 'false', 'no', 'off'], true)) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'disabled_by_env'];
    }

    [$root, $triedHubspotPaths] = pd_hubspot_api_root_resolve();
    if ($root === null) {
        if (function_exists('pd_log')) {
            pd_log('hubspot_api_folder_missing', ['tried' => $triedHubspotPaths]);
        }
        return [
            'ok' => false,
            'skipped' => true,
            'reason' => 'hubspot_api_folder_missing',
            'hint' => 'Set HUBSPOT_API_ROOT in env or define in php/config.php to the absolute server path of the folder containing hubspot-config.php (the /hubspot/api directory). Check php/log for tried paths.',
            'tried_paths' => $triedHubspotPaths,
        ];
    }

    $configFile = $root . '/hubspot-config.php';
    $apiFile = $root . '/includes/hubspot-api.php';
    if (!is_file($configFile) || !is_file($apiFile)) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_config_or_includes_missing'];
    }

    require_once $configFile;
    require_once $apiFile;

    if (!defined('HUBSPOT_PRIVATE_APP_TOKEN')) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_token_undefined'];
    }
    $tok = (string) HUBSPOT_PRIVATE_APP_TOKEN;
    if ($tok === '' || $tok === 'YOUR_PRIVATE_APP_TOKEN_HERE') {
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_token_not_configured'];
    }

    $viewUrl = 'https://app.pandadoc.com/a/#/documents/' . rawurlencode($pandadocDocumentId);
    $shopifyIdStr = (string) $shopifyOrderId;

    // --- Primary: HubSpot Order object ---
    $orderPropUrl = defined('HUBSPOT_ORDER_PROPERTY_PANDADOC_DOCUMENT_URL')
        ? trim((string) HUBSPOT_ORDER_PROPERTY_PANDADOC_DOCUMENT_URL)
        : '';
    if ($orderPropUrl !== '') {
        $hubspotOrderId = hubspot_find_order_id_by_shopify_order($shopifyIdStr);
        if ($hubspotOrderId !== null) {
            $props = [$orderPropUrl => $viewUrl];
            $orderPropId = defined('HUBSPOT_ORDER_PROPERTY_PANDADOC_DOCUMENT_ID')
                ? trim((string) HUBSPOT_ORDER_PROPERTY_PANDADOC_DOCUMENT_ID)
                : '';
            if ($orderPropId !== '') {
                $props[$orderPropId] = $pandadocDocumentId;
            }
            $out = hubspot_update_order_properties($hubspotOrderId, $props);
            if ($out['ok']) {
                $result = ['ok' => true, 'order_id' => $hubspotOrderId, 'status' => $out['status']];
                $fallbackDeal = strtolower(trim((string)(function_exists('pd_env') ? pd_env('HUBSPOT_PANDADOC_FALLBACK_TO_DEAL', '0') : (getenv('HUBSPOT_PANDADOC_FALLBACK_TO_DEAL') ?: '0'))));
                if (in_array($fallbackDeal, ['1', 'true', 'yes', 'on'], true)) {
                    $dealExtra = pd_hubspot_patch_deal_pandadoc($shopifyIdStr, $viewUrl, $pandadocDocumentId);
                    $result['deal_fallback'] = $dealExtra;
                }
                return $result;
            }
            return [
                'ok' => false,
                'order_id' => $hubspotOrderId,
                'status' => $out['status'] ?? 0,
                'error' => $out['error'] ?? 'hubspot_order_patch_failed',
            ];
        }
    }

    // No matching HubSpot order
    $fallbackDeal = strtolower(trim((string)(function_exists('pd_env') ? pd_env('HUBSPOT_PANDADOC_FALLBACK_TO_DEAL', '0') : (getenv('HUBSPOT_PANDADOC_FALLBACK_TO_DEAL') ?: '0'))));
    if (in_array($fallbackDeal, ['1', 'true', 'yes', 'on'], true)) {
        return pd_hubspot_patch_deal_pandadoc($shopifyIdStr, $viewUrl, $pandadocDocumentId);
    }

    return ['ok' => false, 'skipped' => true, 'reason' => 'no_hubspot_order_for_shopify_order'];
}

/**
 * After PandaDoc completed PDF is cleaned, upload it (optional) and set Order.pandadoc_clean_pdf_url.
 *
 * @return array{ok: bool, skipped?: bool, reason?: string, order_id?: string, status?: int, error?: string, clean_pdf_url?: string, upload?: mixed}
 */
function pd_hubspot_on_pandadoc_clean_pdf_ready(int $shopifyOrderId, string $cleanPdfPath): array {
    $cleanPdfPath = trim($cleanPdfPath);
    if ($cleanPdfPath === '' || !is_file($cleanPdfPath)) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: clean_pdf_missing', ['order_id' => $shopifyOrderId, 'path' => $cleanPdfPath]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'clean_pdf_missing'];
    }

    $sync = strtolower(trim((string) (function_exists('pd_env') ? pd_env('HUBSPOT_SYNC_PANDADOC', '1') : (getenv('HUBSPOT_SYNC_PANDADOC') ?: '1'))));
    if (in_array($sync, ['0', 'false', 'no', 'off'], true)) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: skipped HUBSPOT_SYNC_PANDADOC off', ['order_id' => $shopifyOrderId]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'disabled_by_env'];
    }

    [$root, $triedHubspotPaths] = pd_hubspot_api_root_resolve();
    if ($root === null) {
        if (function_exists('pd_log')) {
            pd_log('hubspot_api_folder_missing_clean_pdf', ['tried' => $triedHubspotPaths]);
        }
        return [
            'ok' => false,
            'skipped' => true,
            'reason' => 'hubspot_api_folder_missing',
            'hint' => 'Set HUBSPOT_API_ROOT to the folder containing hubspot-config.php.',
            'tried_paths' => $triedHubspotPaths,
        ];
    }

    $configFile = $root . '/hubspot-config.php';
    $apiFile = $root . '/includes/hubspot-api.php';
    if (!is_file($configFile) || !is_file($apiFile)) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: hubspot_config_or_includes_missing', ['order_id' => $shopifyOrderId, 'root' => $root]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_config_or_includes_missing'];
    }

    require_once $configFile;
    require_once $apiFile;

    if (!defined('HUBSPOT_PRIVATE_APP_TOKEN')) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: hubspot_token_undefined', ['order_id' => $shopifyOrderId]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_token_undefined'];
    }
    $tok = (string) HUBSPOT_PRIVATE_APP_TOKEN;
    if ($tok === '' || $tok === 'YOUR_PRIVATE_APP_TOKEN_HERE') {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: hubspot_token_not_configured', ['order_id' => $shopifyOrderId]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'hubspot_token_not_configured'];
    }

    $prop = pd_hubspot_order_property_pandadoc_clean_pdf_url();
    if ($prop === '') {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: clean_pdf_property_blank', ['order_id' => $shopifyOrderId]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'clean_pdf_property_blank'];
    }

    $shopifyIdStr = (string) $shopifyOrderId;
    $hubspotOrderId = hubspot_find_order_id_by_shopify_order($shopifyIdStr);
    if ($hubspotOrderId === null) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: no_hubspot_order_for_shopify_order', ['order_id' => $shopifyOrderId, 'shopify_id' => $shopifyIdStr]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'no_hubspot_order_for_shopify_order'];
    }

    $doUpload = strtolower(trim((string) (function_exists('pd_env') ? pd_env('HUBSPOT_UPLOAD_CLEAN_PDF', '1') : (getenv('HUBSPOT_UPLOAD_CLEAN_PDF') ?: '1'))));
    if (in_array($doUpload, ['0', 'false', 'no', 'off'], true)) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: upload_disabled (HUBSPOT_UPLOAD_CLEAN_PDF)', ['order_id' => $shopifyOrderId]);
        }
        return ['ok' => false, 'skipped' => true, 'reason' => 'upload_disabled'];
    }

    $folderPath = (string) (function_exists('pd_env') ? pd_env('HUBSPOT_FILES_FOLDER_PATH', '/pandadoc') : (getenv('HUBSPOT_FILES_FOLDER_PATH') ?: '/pandadoc'));
    $access = (string) (function_exists('pd_env') ? pd_env('HUBSPOT_FILES_ACCESS', 'PUBLIC_NOT_INDEXABLE') : (getenv('HUBSPOT_FILES_ACCESS') ?: 'PUBLIC_NOT_INDEXABLE'));
    $fileName = 'order_' . $shopifyOrderId . '_signed.pdf';
    $upload = pd_hubspot_files_upload($tok, $cleanPdfPath, $folderPath, $access, $fileName);
    if (empty($upload['ok'])) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: Files API upload failed', [
                'order_id' => $shopifyOrderId,
                'hubspot_order_id' => $hubspotOrderId,
                'file' => $fileName,
                'folderPath' => $folderPath,
                'upload' => $upload,
            ]);
        }
        return ['ok' => false, 'order_id' => $hubspotOrderId, 'status' => $upload['status'] ?? 0, 'error' => $upload['error'] ?? 'hubspot_upload_failed', 'upload' => $upload];
    }
    $fileUrl = (string)($upload['url'] ?? '');
    if ($fileUrl === '') {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: upload OK but no URL in response', [
                'order_id' => $shopifyOrderId,
                'hubspot_order_id' => $hubspotOrderId,
                'upload_raw' => $upload['raw'] ?? $upload,
            ]);
        }
        return ['ok' => false, 'order_id' => $hubspotOrderId, 'status' => $upload['status'] ?? 0, 'error' => 'hubspot_upload_missing_url', 'upload' => $upload];
    }

    $out = hubspot_update_order_properties($hubspotOrderId, [$prop => $fileUrl]);
    if (!empty($out['ok'])) {
        if (function_exists('pd_log')) {
            pd_log('HubSpot clean PDF: Order property updated', [
                'order_id' => $shopifyOrderId,
                'hubspot_order_id' => $hubspotOrderId,
                'property' => $prop,
                'clean_pdf_url' => $fileUrl,
                'patch_status' => $out['status'] ?? null,
            ]);
        }
        return ['ok' => true, 'order_id' => $hubspotOrderId, 'status' => $out['status'], 'clean_pdf_url' => $fileUrl, 'upload' => $upload];
    }
    if (function_exists('pd_log')) {
        pd_log('HubSpot clean PDF: Order property patch failed', [
            'order_id' => $shopifyOrderId,
            'hubspot_order_id' => $hubspotOrderId,
            'property' => $prop,
            'clean_pdf_url' => $fileUrl,
            'patch' => $out,
        ]);
    }
    return [
        'ok' => false,
        'order_id' => $hubspotOrderId,
        'status' => $out['status'] ?? 0,
        'error' => $out['error'] ?? 'hubspot_order_patch_failed',
        'clean_pdf_url' => $fileUrl,
        'upload' => $upload,
    ];
}

/**
 * @return array{ok: bool, skipped?: bool, reason?: string, deal_id?: string, status?: int, error?: string}
 */
function pd_hubspot_patch_deal_pandadoc(string $shopifyOrderId, string $viewUrl, string $pandadocDocumentId): array {
    $propUrl = defined('HUBSPOT_DEAL_PROPERTY_PANDADOC_DOCUMENT_URL')
        ? trim((string) HUBSPOT_DEAL_PROPERTY_PANDADOC_DOCUMENT_URL)
        : '';
    if ($propUrl === '') {
        return ['ok' => false, 'skipped' => true, 'reason' => 'deal_pandadoc_url_property_blank'];
    }
    $dealId = hubspot_find_deal_id_by_shopify_order($shopifyOrderId);
    if ($dealId === null) {
        return ['ok' => false, 'skipped' => true, 'reason' => 'no_hubspot_deal_for_shopify_order'];
    }
    $props = [$propUrl => $viewUrl];
    $propId = defined('HUBSPOT_DEAL_PROPERTY_PANDADOC_DOCUMENT_ID')
        ? trim((string) HUBSPOT_DEAL_PROPERTY_PANDADOC_DOCUMENT_ID)
        : '';
    if ($propId !== '') {
        $props[$propId] = $pandadocDocumentId;
    }
    $out = hubspot_update_deal_properties($dealId, $props);
    if ($out['ok']) {
        return ['ok' => true, 'deal_id' => $dealId, 'status' => $out['status']];
    }
    return [
        'ok' => false,
        'deal_id' => $dealId,
        'status' => $out['status'] ?? 0,
        'error' => $out['error'] ?? 'hubspot_deal_patch_failed',
    ];
}
