<?php
/**
 * Paradise Group WhatsApp Bot - Enhanced Version
 * Version: 2.0 | Date: 2024-07-06
 */


require_once __DIR__ . '/config.php';




// ======== Directory Creation (Best Practice & Error Safe) ========

if (isset($config['dirs']) && is_array($config['dirs'])) {
    foreach ($config['dirs'] as $dir) {
        if (!is_dir($dir)) {
            // Create directory with permissions if not exists
            @mkdir($dir, 0755, true);
        }
    }
}

// ======== Enhanced Logging System ========
function logMessage(string $message, string $type = 'INFO', string $user_id = null): void {
    global $config;
    $timestamp = date('Y-m-d H:i:s');
    $prefix = "[$timestamp] [$type]" . ($user_id ? " [User: $user_id]" : '');
    $entry = "$prefix $message\n";
    
    $logFiles = [
        'ERROR'      => 'logs/errors.log',
        'WARNING'    => 'logs/warnings.log',
        'RATE_LIMIT' => 'logs/rate_limits.log',
        'CONVERSION' => 'logs/conversions.log',
        'API'        => 'logs/api_requests.log'
    ];
    
    $filepath = $logFiles[$type] ?? 'logs/activity.log';
    // Ensure log directory exists
    if (!is_dir(dirname($filepath))) {
        @mkdir(dirname($filepath), 0755, true);
    }
    @file_put_contents($filepath, $entry, FILE_APPEND | LOCK_EX);
}

// ======== Security Headers (should be sent only ONCE per request) ========
// NOTE: It's best to send headers from your main entry script only ONCE!
// Here for reference, but don't repeat in every include:
if (!headers_sent()) {
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    header('X-Frame-Options: DENY');
    header('X-XSS-Protection: 1; mode=block');
    header('Strict-Transport-Security: max-age=31536000');
}

// ======== Input Validation ========
function validateInput($input): bool {
    $input = $input ?? '';
    $trimmed = trim($input);
    return !empty($trimmed) &&
           mb_strlen($trimmed, 'UTF-8') <= 2000 &&
           mb_strlen($trimmed, 'UTF-8') >= 1;
}

function sanitizeInput($input): string {
    $input = $input ?? '';
    $input = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $input);
    $input = preg_replace('/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/i', '', $input);
    return trim(htmlspecialchars($input, ENT_QUOTES, 'UTF-8'));
}

// ======== Defensive Helper for All Inputs ========
function safeInput($input): string {
    return isset($input) && $input !== null ? (string)$input : '';
}

function normalizeDigitsToAscii(string $text): string {
    return strtr($text, [
        '٠' => '0', '١' => '1', '٢' => '2', '٣' => '3', '٤' => '4',
        '٥' => '5', '٦' => '6', '٧' => '7', '٨' => '8', '٩' => '9',
        '۰' => '0', '۱' => '1', '۲' => '2', '۳' => '3', '۴' => '4',
        '۵' => '5', '۶' => '6', '۷' => '7', '۸' => '8', '۹' => '9',
    ]);
}

function normalizeEgyptWhatsAppNumber(string $raw): ?string {
    $raw = normalizeDigitsToAscii($raw);
    $digits = preg_replace('/\D+/', '', $raw);
    if ($digits === '') return null;

    if (strpos($digits, '00') === 0) {
        $digits = substr($digits, 2);
    }

    // 01XXXXXXXXX -> 201XXXXXXXXX
    if (preg_match('/^01\d{9}$/', $digits)) {
        return '2' . $digits;
    }
    // 1XXXXXXXXX -> 201XXXXXXXXX
    if (preg_match('/^1\d{9}$/', $digits)) {
        return '20' . $digits;
    }
    // already E.164 digits for Egypt mobile
    if (preg_match('/^201\d{9}$/', $digits)) {
        return $digits;
    }

    return null;
}

function extractPossibleWhatsAppNumber(string $text): ?string {
    $text = normalizeDigitsToAscii($text);
    if (trim($text) === '') return null;

    $matches = [];
    preg_match_all('/(\+?\d[\d\-\s\(\)]{7,18}\d)/u', $text, $matches);
    $candidates = $matches[1] ?? [];

    // Handle plain input that may come without separators
    if (empty($candidates)) {
        $candidates = [$text];
    }

    foreach ($candidates as $candidate) {
        $normalized = normalizeEgyptWhatsAppNumber($candidate);
        if ($normalized !== null) {
            return $normalized;
        }
    }

    return null;
}

function getPsidMapFilePath(): string {
    $dir = 'customers/maps';
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }
    return $dir . '/psid_map.json';
}

function loadPsidMap(): array {
    $file = getPsidMapFilePath();
    if (!is_file($file)) return [];
    $map = json_decode((string)@file_get_contents($file), true);
    return is_array($map) ? $map : [];
}

function savePsidMap(array $map): void {
    $file = getPsidMapFilePath();
    @file_put_contents($file, json_encode($map, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
}

function mergeUniqueStringArray(array $a, array $b): array {
    $merged = [];
    foreach (array_merge($a, $b) as $item) {
        if (!is_scalar($item)) continue;
        $v = trim((string)$item);
        if ($v !== '') $merged[$v] = true;
    }
    return array_keys($merged);
}

function mergeConversationLogs(array $existing, array $incoming): array {
    $combined = array_merge($existing, $incoming);
    usort($combined, function ($x, $y) {
        $tx = (int)($x['timestamp'] ?? ($x['ts'] ?? 0));
        $ty = (int)($y['timestamp'] ?? ($y['ts'] ?? 0));
        if ($tx === $ty) return 0;
        return $tx <=> $ty;
    });
    return $combined;
}

function mergeCustomerRecords(array $current, array $legacy, string $canonicalId): array {
    $defaults = getDefaultCustomerData($canonicalId);
    $merged = array_merge($defaults, $legacy, $current);
    $merged['phone'] = $canonicalId;
    $merged['wa_phone'] = $canonicalId;

    foreach ([
        'name', 'city', 'place_type', 'business_type', 'status', 'qualification_status',
        'note', 'custom_details', 'wa_phone', 'project_scope', 'budget_range',
        'project_timeline', 'lead_source', 'project_city', 'project_place_type', 'project_services'
    ] as $field) {
        $currentVal = trim((string)($merged[$field] ?? ''));
        $legacyVal = trim((string)($legacy[$field] ?? ''));
        if ($currentVal === '' && $legacyVal !== '') {
            $merged[$field] = $legacyVal;
        }
    }

    $merged['services'] = mergeUniqueStringArray((array)($legacy['services'] ?? []), (array)($current['services'] ?? []));
    $merged['interests'] = mergeUniqueStringArray((array)($legacy['interests'] ?? []), (array)($current['interests'] ?? []));
    $merged['identity_aliases'] = mergeUniqueStringArray(
        array_merge((array)($legacy['identity_aliases'] ?? []), [$legacy['phone'] ?? '']),
        array_merge((array)($current['identity_aliases'] ?? []), [$current['phone'] ?? ''])
    );

    $legacyNotes = is_array($legacy['notes'] ?? null) ? $legacy['notes'] : [];
    $currentNotes = is_array($current['notes'] ?? null) ? $current['notes'] : [];
    $merged['notes'] = array_slice(mergeConversationLogs($legacyNotes, $currentNotes), -200);

    $merged['conversation_count'] = max(
        (int)($current['conversation_count'] ?? 0),
        (int)($legacy['conversation_count'] ?? 0),
        count($merged['notes'])
    );
    $merged['lead_score'] = max((int)($legacy['lead_score'] ?? 0), (int)($current['lead_score'] ?? 0));

    $legacyCreated = (int)($legacy['created_at'] ?? 0);
    $currentCreated = (int)($current['created_at'] ?? 0);
    $merged['created_at'] = min(
        $legacyCreated > 0 ? $legacyCreated : PHP_INT_MAX,
        $currentCreated > 0 ? $currentCreated : PHP_INT_MAX
    );
    if ($merged['created_at'] === PHP_INT_MAX) $merged['created_at'] = time();

    $merged['last_contact'] = max((int)($legacy['last_contact'] ?? 0), (int)($current['last_contact'] ?? 0), time());

    $legacySignals = is_array($legacy['conversion_signals'] ?? null) ? $legacy['conversion_signals'] : [];
    $currentSignals = is_array($current['conversion_signals'] ?? null) ? $current['conversion_signals'] : [];
    $signalMap = [];
    foreach (array_merge($legacySignals, $currentSignals) as $sig) {
        $k = md5(json_encode($sig, JSON_UNESCAPED_UNICODE));
        $signalMap[$k] = $sig;
    }
    $merged['conversion_signals'] = array_values($signalMap);

    return $merged;
}

function migrateCustomerIdentity(string $oldId, string $newId): void {
    $oldId = basename(trim($oldId));
    $newId = basename(trim($newId));
    if ($oldId === '' || $newId === '' || $oldId === $newId) return;

    $oldCustomerFile = "customers/{$oldId}.json";
    $newCustomerFile = "customers/{$newId}.json";
    $oldChatFile = "chats/{$oldId}.json";
    $newChatFile = "chats/{$newId}.json";

    $legacyCustomer = [];
    $currentCustomer = [];
    if (is_file($oldCustomerFile)) {
        $legacyCustomer = json_decode((string)@file_get_contents($oldCustomerFile), true);
        if (!is_array($legacyCustomer)) $legacyCustomer = [];
    }
    if (is_file($newCustomerFile)) {
        $currentCustomer = json_decode((string)@file_get_contents($newCustomerFile), true);
        if (!is_array($currentCustomer)) $currentCustomer = [];
    }

    if (!empty($legacyCustomer) || !empty($currentCustomer)) {
        $mergedCustomer = mergeCustomerRecords($currentCustomer, $legacyCustomer, $newId);
        @file_put_contents($newCustomerFile, json_encode($mergedCustomer, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
    }
    if (is_file($oldCustomerFile) && $oldCustomerFile !== $newCustomerFile) {
        @unlink($oldCustomerFile);
    }

    $legacyChat = [];
    $currentChat = [];
    if (is_file($oldChatFile)) {
        $legacyChat = json_decode((string)@file_get_contents($oldChatFile), true);
        if (!is_array($legacyChat)) $legacyChat = [];
    }
    if (is_file($newChatFile)) {
        $currentChat = json_decode((string)@file_get_contents($newChatFile), true);
        if (!is_array($currentChat)) $currentChat = [];
    }

    if (!empty($legacyChat) || !empty($currentChat)) {
        $mergedChat = mergeConversationLogs($legacyChat, $currentChat);
        @file_put_contents($newChatFile, json_encode($mergedChat, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
    }
    if (is_file($oldChatFile) && $oldChatFile !== $newChatFile) {
        @unlink($oldChatFile);
    }
}

function resolveIncomingCustomerIdentity(string $platform, string $from, string $userInput): array {
    global $config;

    $out = [
        'customer_id' => $from,
        'wa_phone' => '',
        'has_wa_phone' => false,
        'needs_wa_phone' => false,
        'linked_now' => false,
    ];

    if ($from === '') return $out;

    if ($platform === 'whatsapp') {
        $wa = preg_replace('/\D+/', '', $from);
        $out['customer_id'] = $wa !== '' ? $wa : $from;
        $out['wa_phone'] = $wa;
        $out['has_wa_phone'] = ($wa !== '');
        return $out;
    }

    $captureEnabled = (bool)($config['messenger_require_wa_phone'] ?? true);
    $capturePlatforms = $config['messenger_wa_capture_platforms'] ?? ['messenger'];
    if (!is_array($capturePlatforms) || empty($capturePlatforms)) $capturePlatforms = ['messenger'];

    if (!$captureEnabled || !in_array($platform, $capturePlatforms, true)) {
        return $out;
    }

    $map = loadPsidMap();
    $entry = isset($map[$from]) && is_array($map[$from]) ? $map[$from] : [];
    $mappedWa = normalizeEgyptWhatsAppNumber((string)($entry['wa_phone'] ?? ''));
    if ($mappedWa !== null) {
        migrateCustomerIdentity($from, $mappedWa);

        $entry['wa_phone'] = $mappedWa;
        $entry['platform'] = $platform;
        $entry['last_seen'] = time();
        $map[$from] = $entry;
        savePsidMap($map);

        $out['customer_id'] = $mappedWa;
        $out['wa_phone'] = $mappedWa;
        $out['has_wa_phone'] = true;
        return $out;
    }

    $capturedWa = extractPossibleWhatsAppNumber($userInput);
    if ($capturedWa !== null) {
        $entry['wa_phone'] = $capturedWa;
        $entry['platform'] = $platform;
        $entry['linked_at'] = (int)($entry['linked_at'] ?? time());
        $entry['last_seen'] = time();
        $entry['source'] = 'captured_message';
        $map[$from] = $entry;
        savePsidMap($map);

        migrateCustomerIdentity($from, $capturedWa);

        $out['customer_id'] = $capturedWa;
        $out['wa_phone'] = $capturedWa;
        $out['has_wa_phone'] = true;
        $out['linked_now'] = true;
        return $out;
    }

    $out['needs_wa_phone'] = true;
    return $out;
}

function buildWaPhoneCapturePrompt(bool $english = false): string {
    if ($english) {
        return "Welcome to Paradise Group ✨ We can continue here on Messenger, and for faster follow-up on WhatsApp please share your number in this format: 01XXXXXXXXX or 201XXXXXXXXX.";
    }
    return "أهلاً بحضرتك في Paradise Group ✨ يسعدنا نكمل معاك هنا على ماسنجر، ولو تحب متابعة أسرع على واتساب ابعت رقمك بصيغة 01XXXXXXXXX أو 201XXXXXXXXX.";
}

function buildWaPhoneCaptureQuickReplies(bool $english = false): array {
    if ($english) {
        return [
            ['title' => 'Send My Number', 'payload' => 'send_my_number'],
            ['title' => 'Continue Here', 'payload' => 'continue_here'],
            ['title' => 'Need Quick Call', 'payload' => 'need_quick_call'],
        ];
    }
    return [
        ['title' => 'إرسال رقمي', 'payload' => 'send_my_number'],
        ['title' => 'نكمل هنا', 'payload' => 'continue_here'],
        ['title' => 'أحتاج اتصال', 'payload' => 'need_quick_call'],
    ];
}

function shouldSendWaPhoneCapturePrompt(array $customer): bool {
    $now = time();
    $last = (int)($customer['wa_phone_prompt_last_sent'] ?? 0);
    $count = (int)($customer['wa_phone_prompt_count'] ?? 0);
    $cooldown = 1200; // 20 minutes

    if ($last <= 0) return true;
    if (($now - $last) >= $cooldown) return true;
    if ($count <= 1 && ($now - $last) >= 300) return true; // allow one early reminder
    return false;
}

function buildWaPhoneCapturedAck(bool $english = false): string {
    if ($english) {
        return "Thanks, your WhatsApp number is saved ✅ Is your request for new execution or maintenance?";
    }
    return "تم تسجيل رقم واتسابك بنجاح ✅ تحب الخدمة تكون تنفيذ جديد ولا صيانة؟";
}

function customerServicesText(array $customer): string {
    $services = $customer['services'] ?? [];
    if (is_array($services)) {
        $clean = [];
        foreach ($services as $s) {
            if (!is_scalar($s)) continue;
            $v = trim((string)$s);
            if ($v !== '' && strtolower($v) !== 'array') {
                $clean[$v] = true;
            }
        }
        if (!empty($clean)) {
            return implode('، ', array_keys($clean));
        }
    } elseif (is_scalar($services)) {
        $v = trim((string)$services);
        if ($v !== '' && strtolower($v) !== 'array') {
            return $v;
        }
    }

    $fallback = trim((string)($customer['project_services'] ?? ''));
    return strtolower($fallback) === 'array' ? '' : $fallback;
}

function extractBudgetFromInput(string $text): ?string {
    $text = trim(normalizeDigitsToAscii($text));
    if ($text === '') return null;

    $normalized = normalizeIntentInput($text);
    $hasBudgetKeywords = containsAnyPhrase($normalized, [
        'ميزانية', 'budget', 'egp', 'جنيه', 'الف', 'ألف', 'مليون',
        'cost', 'تكلفة', 'تكلفه', 'price', 'سعر'
    ]);

    preg_match_all('/\d{2,9}/', $text, $m);
    $hasNumbers = !empty($m[0]);
    $looksLikeStandaloneAmount = (bool)preg_match('/^\s*[\d\.,]+\s*(k|m|الف|ألف|مليون)?\s*$/iu', $text);
    if (!$hasBudgetKeywords && !$looksLikeStandaloneAmount) {
        return null;
    }

    $digitsOnly = preg_replace('/\D+/', '', $text);
    if (($hasNumbers || $looksLikeStandaloneAmount) && preg_match('/^(?:201\d{9}|01\d{9}|1\d{9})$/', (string)$digitsOnly)) {
        return null; // likely a phone number, not budget
    }

    $clean = preg_replace('/\s+/u', ' ', $text);
    return mb_substr(trim($clean), 0, 120, 'UTF-8');
}

function extractTimelineFromInput(string $text): ?string {
    $text = trim(normalizeDigitsToAscii($text));
    if ($text === '') return null;

    $normalized = normalizeIntentInput($text);
    $hasTimelineKeywords = containsAnyPhrase($normalized, [
        'خلال', 'بعد', 'موعد', 'فور', 'فورا', 'فوراً', 'عاجل', 'حالاً',
        'اسبوع', 'أسبوع', 'شهر', 'يوم', 'سنة',
        'timeline', 'urgent', 'asap', 'week', 'month', 'day'
    ]);
    $hasDurationPattern = (bool)preg_match('/\d+\s*(يوم|ايام|أيام|اسبوع|أسبوع|شهر|سنة|day|days|week|weeks|month|months|year|years)/iu', $text);
    if (!$hasTimelineKeywords && !$hasDurationPattern) {
        return null;
    }

    $clean = preg_replace('/\s+/u', ' ', $text);
    return mb_substr(trim($clean), 0, 120, 'UTF-8');
}

function extractProjectScopeFromInput(string $text): ?string {
    $normalized = normalizeIntentInput($text);
    if ($normalized === '') return null;

    if (containsAnyPhrase($normalized, ['صيانه', 'صيانة', 'maintenance', 'repair', 'fix'])) {
        return 'صيانة';
    }
    if (containsAnyPhrase($normalized, ['تنفيذ جديد', 'تنفيذ', 'new', 'new build', 'new project'])) {
        return 'تنفيذ جديد';
    }
    if (containsAnyPhrase($normalized, ['تصميم', 'design', '3d', '2d'])) {
        return 'تصميم';
    }
    return null;
}

function updateSalesProfileFromInput(array &$customer, string $input, string $platform = ''): array {
    $updated = [];
    $now = time();

    if (empty($customer['sales_flow_started_at'])) {
        $customer['sales_flow_started_at'] = $now;
        $updated[] = 'sales_flow_started_at';
    }

    if ($platform !== '' && empty($customer['lead_source'])) {
        $customer['lead_source'] = $platform;
        $updated[] = 'lead_source';
    }

    if (!empty($customer['wa_phone']) && empty($customer['contact_phone'])) {
        $customer['contact_phone'] = (string)$customer['wa_phone'];
        $updated[] = 'contact_phone';
    }

    if (!empty($customer['city']) && empty($customer['project_city'])) {
        $customer['project_city'] = (string)$customer['city'];
        $updated[] = 'project_city';
    }
    if (!empty($customer['place_type']) && empty($customer['project_place_type'])) {
        $customer['project_place_type'] = (string)$customer['place_type'];
        $updated[] = 'project_place_type';
    }

    $servicesText = customerServicesText($customer);
    if ($servicesText !== '' && empty($customer['project_services'])) {
        $customer['project_services'] = $servicesText;
        $updated[] = 'project_services';
    }

    $scope = extractProjectScopeFromInput($input);
    if ($scope !== null && trim((string)($customer['project_scope'] ?? '')) !== $scope) {
        $customer['project_scope'] = $scope;
        $updated[] = 'project_scope';
    }

    $budget = extractBudgetFromInput($input);
    if ($budget !== null && trim((string)($customer['budget_range'] ?? '')) !== $budget) {
        $customer['budget_range'] = $budget;
        $updated[] = 'budget_range';
    }

    $timeline = extractTimelineFromInput($input);
    if ($timeline !== null && trim((string)($customer['project_timeline'] ?? '')) !== $timeline) {
        $customer['project_timeline'] = $timeline;
        $updated[] = 'project_timeline';
    }

    return array_values(array_unique($updated));
}

function getNextSalesQuestionField(array $customer): ?string {
    if (trim((string)($customer['city'] ?? ($customer['project_city'] ?? ''))) === '') return 'city';
    if (trim((string)($customer['place_type'] ?? ($customer['project_place_type'] ?? ''))) === '') return 'place_type';
    if (customerServicesText($customer) === '') return 'services';
    if (trim((string)($customer['budget_range'] ?? '')) === '') return 'budget_range';
    if (trim((string)($customer['project_timeline'] ?? '')) === '') return 'project_timeline';
    return null;
}

function buildSalesQuestionForField(string $field, bool $english = false): string {
    $ar = [
        'city' => 'ممتاز. المشروع في أي مدينة/منطقة؟',
        'place_type' => 'ونوع المكان إيه؟ (فيلا / منزل / شركة / نادي)',
        'services' => 'محتاج أنهي خدمة تحديدًا؟ (حمام سباحة / لاندسكيب / شلالات / صيانة)',
        'budget_range' => 'الميزانية التقريبية المتاحة كام؟',
        'project_timeline' => 'حابب التنفيذ يبدأ إمتى؟',
    ];
    $en = [
        'city' => 'Great. Which city/area is the project in?',
        'place_type' => 'What is the place type (villa, home, company, club)?',
        'services' => 'Which service do you need exactly (pool, landscape, water features, maintenance)?',
        'budget_range' => 'What is your approximate budget range?',
        'project_timeline' => 'When would you like execution to start?',
    ];
    $map = $english ? $en : $ar;
    return $map[$field] ?? ($english ? 'Could you share one more project detail?' : 'ممكن تشاركنا تفصيلة إضافية عن المشروع؟');
}

function ensureMessengerSalesFollowup(string $text, array &$customer, string $platform, string $userInput, bool $forceAsk = false): string {
    if ($platform !== 'messenger') return $text;

    $field = getNextSalesQuestionField($customer);
    if ($field === null) {
        if (empty($customer['sales_flow_completed_at'])) {
            $customer['sales_flow_completed_at'] = time();
        }
        return $text;
    }

    $customer['sales_last_question_field'] = $field;
    $customer['sales_last_question_at'] = time();
    $question = buildSalesQuestionForField($field, isEnglishOnlyInput($userInput));

    if (!$forceAsk && preg_match('/[؟?]/u', $text)) {
        return $text;
    }
    if (mb_strpos($text, $question) !== false) {
        return $text;
    }
    if (trim($text) === '') {
        return $question;
    }
    return rtrim($text) . "\n\n" . $question;
}

function buildSalesSummaryLine(array $customer): string {
    $parts = [];
    $city = trim((string)($customer['city'] ?? ($customer['project_city'] ?? '')));
    $placeType = trim((string)($customer['place_type'] ?? ($customer['project_place_type'] ?? '')));
    $services = customerServicesText($customer);
    $scope = trim((string)($customer['project_scope'] ?? ''));
    $budget = trim((string)($customer['budget_range'] ?? ''));
    $timeline = trim((string)($customer['project_timeline'] ?? ''));

    if ($city !== '') $parts[] = "المدينة: {$city}";
    if ($placeType !== '') $parts[] = "نوع المكان: {$placeType}";
    if ($services !== '') $parts[] = "الخدمة: {$services}";
    if ($scope !== '') $parts[] = "نوع الطلب: {$scope}";
    if ($budget !== '') $parts[] = "الميزانية: {$budget}";
    if ($timeline !== '') $parts[] = "التنفيذ: {$timeline}";

    return implode(' | ', $parts);
}

function acquireExclusiveFileLock(string $lockPath, bool $nonBlocking = false) {
    $fh = @fopen($lockPath, 'c');
    if (!$fh) return null;
    $flags = LOCK_EX | ($nonBlocking ? LOCK_NB : 0);
    if (!@flock($fh, $flags)) {
        @fclose($fh);
        return null;
    }
    return $fh;
}

function releaseExclusiveFileLock($fh): void {
    if (is_resource($fh)) {
        @flock($fh, LOCK_UN);
        @fclose($fh);
    }
}

function extractCommentTextValue(array $value): string {
    $textFields = ['message', 'text', 'comment_text', 'body', 'content'];
    foreach ($textFields as $textField) {
        if (!empty($value[$textField]) && is_string($value[$textField])) {
            $txt = trim($value[$textField]);
            if ($txt !== '') return $txt;
        }
    }
    return '';
}

function extractCommentAuthorInfo(array $value): array {
    $fromId = '';
    $fromName = '';
    $fromUsername = '';

    if (isset($value['from'])) {
        if (is_array($value['from'])) {
            $fromId = trim((string)($value['from']['id'] ?? ''));
            $fromName = trim((string)($value['from']['name'] ?? ''));
            $fromUsername = trim((string)($value['from']['username'] ?? ''));
        } else {
            $fromId = trim((string)$value['from']);
        }
    }

    if ($fromId === '') {
        foreach (['sender_id', 'user_id', 'author_id'] as $idField) {
            if (!empty($value[$idField])) {
                $fromId = trim((string)$value[$idField]);
                if ($fromId !== '') break;
            }
        }
    }
    if ($fromUsername === '') {
        foreach (['username', 'from_username', 'user_name'] as $userField) {
            if (!empty($value[$userField]) && is_string($value[$userField])) {
                $fromUsername = trim((string)$value[$userField]);
                if ($fromUsername !== '') break;
            }
        }
    }
    if ($fromName === '' && $fromUsername !== '') {
        $fromName = $fromUsername;
    }

    return [
        'from_id' => $fromId,
        'from_name' => $fromName,
        'from_username' => $fromUsername,
    ];
}

function extractCommentEventFromChange(array $entry, array $change): ?array {
    $field = strtolower(trim((string)($change['field'] ?? '')));
    $value = (isset($change['value']) && is_array($change['value'])) ? $change['value'] : [];
    if (empty($value)) return null;

    $item = strtolower(trim((string)($value['item'] ?? '')));
    $verb = strtolower(trim((string)($value['verb'] ?? '')));
    $commentId = trim((string)($value['comment_id'] ?? ($value['id'] ?? '')));
    $commentText = extractCommentTextValue($value);

    $isFacebookCommentAdd = (
        $field === 'feed' &&
        $item === 'comment' &&
        $verb === 'add' &&
        $commentId !== ''
    );

    $isInstagramField = in_array($field, ['comments', 'live_comments', 'comment'], true);
    $igVerbOk = ($verb === '' || in_array($verb, ['add', 'created'], true));
    $igItemOk = ($item === '' || $item === 'comment');
    $isInstagramCommentAdd = (
        $isInstagramField &&
        $commentId !== '' &&
        $commentText !== '' &&
        $igVerbOk &&
        $igItemOk
    );

    if (!$isFacebookCommentAdd && !$isInstagramCommentAdd) {
        return null;
    }

    $author = extractCommentAuthorInfo($value);
    $platform = $isFacebookCommentAdd ? 'facebook' : 'instagram';

    return [
        'type'          => 'comment',
        'platform'      => $platform,
        'comment_id'    => $commentId,
        'from_id'       => $author['from_id'],
        'from_name'     => $author['from_name'],
        'from_username' => $author['from_username'],
        'comment_text'  => $commentText,
        'post_id'       => (string)($value['post_id'] ?? ($value['media']['id'] ?? ($value['media_id'] ?? ($value['parent_id'] ?? '')))),
        'parent_id'     => (string)($value['parent_id'] ?? ''),
        'raw_data'      => $value,
        'field'         => $field,
        'item'          => $item,
        'verb'          => $verb,
        'page_id'       => trim((string)($entry['id'] ?? '')),
    ];
}

// ======== Token Management ========
function getOpenAIToken(): string {
    global $openaiTokens;
    static $tokenIndex = 0;
    
    if (empty($openaiTokens)) {
        throw new RuntimeException('No OpenAI tokens available');
    }
    
    $token = $openaiTokens[$tokenIndex];
    $tokenIndex = ($tokenIndex + 1) % count($openaiTokens);
    
    return $token;
}








// ======== Main Execution Flow ========
if (!defined('BOT_TEST_MODE')) {
try {
    $rawInput = file_get_contents('php://input');
    if ($rawInput) {
        @file_put_contents('logs/webhook.log', "[" . date('Y-m-d H:i:s') . "] $rawInput\n", FILE_APPEND | LOCK_EX);
    }

    // Handle webhook verification challenge (GET request)
    if ($_SERVER['REQUEST_METHOD'] === 'GET') {
        handleVerification();
        exit;
    }

    // Ensure POST request
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        http_response_code(405);
        echo json_encode(['error' => 'Method not allowed']);
        exit;
    }

    // Decode WhatsApp payload
    $data = json_decode($rawInput, true) ?? [];
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new Exception('Invalid JSON received: ' . json_last_error_msg());
    }

    // Extract message (ignore if status)
// Extract message (ignore if status)
$message = extractWebhookMessage($data);
if (!$message) {
    echo json_encode(['status' => 'ignored']);
    exit;
}

// ===== معالجة التعليقات أولاً (قبل معالجة الرسائل العادية) =====
if (($message['type'] ?? '') === 'comment') {
    $commentId = trim((string)($message['comment_id'] ?? ''));
    $userId = trim((string)($message['from_id'] ?? ''));
    $pageId = trim((string)($message['page_id'] ?? ''));
    $fromUsername = trim((string)($message['from_username'] ?? ''));
    $platform = (string)($message['platform'] ?? 'facebook');
    $field = (string)($message['field'] ?? '');
    $postId = (string)($message['post_id'] ?? '');
    $item = strtolower(trim((string)($message['item'] ?? '')));
    $verb = strtolower(trim((string)($message['verb'] ?? '')));

    if ($commentId === '') {
        logMessage("Ignore comment event with missing comment_id", 'INFO', $userId);
        echo json_encode(['status' => 'ignored_missing_comment_id']);
        exit;
    }

    if ($pageId !== '' && $userId !== '' && $userId === $pageId) {
        logMessage("Ignore self comment event for page_id={$pageId}, comment_id={$commentId}", 'INFO', $userId);
        echo json_encode(['status' => 'ignored_self_comment']);
        exit;
    }

    // Safety guard: process only real "comment add" events.
    if (
        ($item !== '' && $item !== 'comment') ||
        ($verb !== '' && !in_array($verb, ['add', 'created'], true))
    ) {
        logMessage("Ignore non-actionable comment event: item={$item}, verb={$verb}, field={$field}, id={$commentId}", 'INFO', $userId);
        echo json_encode(['status' => 'ignored_non_actionable_comment_event']);
        exit;
    }

    $stateKey = md5($platform . '_' . $commentId);
    $commentLockFile = "cache/comment_lock_{$stateKey}.lock";
    $commentLockHandle = acquireExclusiveFileLock($commentLockFile, true);
    if (!$commentLockHandle) {
        logMessage("Ignore duplicate webhook while comment lock is held: {$commentId}", 'INFO', $userId);
        echo json_encode(['status' => 'ignored_comment_lock_busy']);
        exit;
    }
    $releaseCommentLock = function () use (&$commentLockHandle): void {
        releaseExclusiveFileLock($commentLockHandle);
    };

    $commentText = trim((string)($message['comment_text'] ?? ''));
    if ($commentText === '' && strpos($commentId, '_') === false) {
        $fetched = fetchCommentText($commentId, $config['facebook_token']);
        if (is_string($fetched)) {
            $commentText = trim($fetched);
        }
    }
    if ($commentText === '') {
        $commentText = 'شكرًا لتعليقك!';
    }

    file_put_contents(
        'logs/comment_processing.log',
        date('Y-m-d H:i:s') . " [{$platform}] Processing comment\n" .
        "Field: {$field}\n" .
        "Item: {$item}\n" .
        "Verb: {$verb}\n" .
        "Comment ID: {$commentId}\n" .
        "User ID: {$userId}\n" .
        "Post ID: {$postId}\n" .
        "Text: '{$commentText}'\n" .
        "Raw data: " . json_encode($message, JSON_UNESCAPED_UNICODE) . "\n\n",
        FILE_APPEND
    );

    // Persistent state file keeps idempotency across webhook retries.
    $commentStateFile = "cache/comment_state_{$stateKey}.json";

    $defaultState = [
        'comment_id'      => $commentId,
        'user_id'         => $userId,
        'platform'        => $platform,
        'field'           => $field,
        'item'            => $item,
        'verb'            => $verb,
        'post_id'         => $postId,
        'comment_text'    => $commentText,
        'like_done'       => false,
        'reply_sent'      => false,
        'dm_sent'         => false,
        'like_attempts'   => 0,
        'reply_attempts'  => 0,
        'dm_attempts'     => 0,
        'ai_reply'        => null,
        'dm_text'         => null,
        'created_at'      => time(),
        'updated_at'      => time(),
    ];
    $commentState = $defaultState;
    if (is_file($commentStateFile)) {
        $storedState = json_decode((string)@file_get_contents($commentStateFile), true);
        if (is_array($storedState)) {
            $commentState = array_merge($commentState, $storedState);
        }
    }
    $saveCommentState = function () use (&$commentState, $commentStateFile): void {
        $commentState['updated_at'] = time();
        @file_put_contents(
            $commentStateFile,
            json_encode($commentState, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
            LOCK_EX
        );
    };

    $commentState['comment_text'] = $commentText;
    $commentState['field'] = $field;
    $commentState['post_id'] = $postId;
    $commentState['item'] = $item;
    $commentState['verb'] = $verb;
    $saveCommentState();

    if ($commentState['reply_sent'] && $commentState['dm_sent'] && $commentState['like_done']) {
        logMessage("Comment already fully processed: {$commentId}", 'INFO', $userId);
        $releaseCommentLock();
        echo json_encode([
            'status' => 'already_processed',
            'platform' => $platform,
            'comment_id' => $commentId,
            'user_id' => $userId,
            'comment_liked' => true,
            'comment_reply_sent' => true,
            'dm_sent' => true,
            'comment_text' => $commentState['comment_text'],
            'ai_reply' => $commentState['ai_reply'],
            'dm_text' => $commentState['dm_text']
        ]);
        exit;
    }

    $replies = buildCommentReplies($commentText, $platform);
    $publicReply = $replies['public'];
    $privateReply = $replies['private'];

    // Exactly one action set per comment event.
    $maxAttempts = ['like' => 1, 'reply' => 1, 'dm' => 1];

    if (!$commentState['like_done']) {
        $attempts = (int)($commentState['like_attempts'] ?? 0);
        if ($attempts < $maxAttempts['like']) {
            $commentState['like_attempts'] = $attempts + 1;
            $saveCommentState();
            if (likeComment($commentId, $config['facebook_token'])) {
                $commentState['like_done'] = true;
                $saveCommentState();
                logMessage("Comment liked successfully", 'INFO', $userId);
            } else {
                logMessage("Comment like failed (attempt {$commentState['like_attempts']})", 'WARNING', $userId);
            }
        } else {
            logMessage("Comment like skipped: max attempts reached", 'WARNING', $userId);
        }
    }

    if (!$commentState['reply_sent']) {
        $attempts = (int)($commentState['reply_attempts'] ?? 0);
        if ($attempts < $maxAttempts['reply']) {
            $commentState['reply_attempts'] = $attempts + 1;
            $saveCommentState();
            $replyWithMention = createReplyWithMention($publicReply, $userId, $platform, $fromUsername);
            if (replyToComment($commentId, $replyWithMention, $config['facebook_token'], $platform)) {
                $commentState['reply_sent'] = true;
                $saveCommentState();
                logMessage("Comment reply sent successfully", 'INFO', $userId);
            } else {
                logMessage("Comment reply failed (attempt {$commentState['reply_attempts']})", 'ERROR', $userId);
            }
        } else {
            logMessage("Comment reply skipped: max attempts reached", 'WARNING', $userId);
        }
    }

    if (!$commentState['dm_sent']) {
        $attempts = (int)($commentState['dm_attempts'] ?? 0);
        if ($attempts < $maxAttempts['dm']) {
            $commentState['dm_attempts'] = $attempts + 1;
            $saveCommentState();

            $sent = false;
            if ($platform === 'instagram') {
                $sent = sendInstagramPrivateReply($commentId, $privateReply, $config['facebook_token']);
            } else {
                $sent = sendFacebookPrivateReply($commentId, $privateReply, $config['facebook_token']);
            }

            if ($sent) {
                $commentState['dm_sent'] = true;
                $saveCommentState();
                logMessage("Private reply sent successfully", 'INFO', $userId);
            } else {
                logMessage("Private reply failed (attempt {$commentState['dm_attempts']})", 'WARNING', $userId);
            }
        } else {
            logMessage("Private reply skipped: max attempts reached", 'WARNING', $userId);
        }
    }

    notifyAdminOnInteraction([
        'event'      => 'comment',
        'platform'   => $platform,
        'from'       => (string)$userId,
        'text'       => (string)$commentText,
        'comment_id' => (string)$commentId,
        'post_id'    => (string)$postId,
        'timestamp'  => time(),
    ]);

    $releaseCommentLock();
    echo json_encode([
        'status' => 'comment_processed',
        'platform' => $platform,
        'comment_liked' => (bool)$commentState['like_done'],
        'comment_reply_sent' => (bool)$commentState['reply_sent'],
        'dm_sent' => (bool)$commentState['dm_sent'],
        'timestamp' => time(),
        'comment_id' => $commentId,
        'user_id' => $userId,
        'comment_text' => $commentState['comment_text'],
        'ai_reply' => $commentState['ai_reply'],
        'dm_text' => $commentState['dm_text']
    ]);
    exit;


}


// باقي الكود للرسائل العادية...
// في أعلى الملف أو بعد require_once:
$forwardNumbers = $config['internal_forward_numbers'] ?? [];
$targetNumber   = $config['internal_target_number'] ?? '';

// ... بعد استخراج الرسالة:
$from = $message['from'] ?? '';
$userInput = extractMessageText($message) ?? '';
$platform = $message['platform'] ?? 'whatsapp';
$identity = resolveIncomingCustomerIdentity($platform, (string)$from, (string)$userInput);
$customerId = (string)($identity['customer_id'] ?? $from);
$identityKey = $customerId !== '' ? $customerId : (string)$from;
// ===== Media handling (WhatsApp + Messenger + Instagram) =====
$incomingMediaMeta = [];

try {
    // WhatsApp media (download by media_id)
    if ($platform === 'whatsapp') {
        $msgType = $message['type'] ?? '';

        // Audio/Voice
        if (in_array($msgType, ['audio','voice'], true)) {
            $mediaId = $message[$msgType]['id'] ?? '';
            if ($mediaId) {
                $download = downloadWhatsAppMedia($mediaId);
                if ($download && !empty($download['path'])) {
                    $incomingMediaMeta['media_files'][] = [
                        'type' => 'audio',
                        'path' => $download['path'],
                        'mime' => $download['mime'] ?? ''
                    ];

                    $transcribed = transcribeAudioOpenAI($download['path']);
                    if ($transcribed) {
                        $userInput = '[صوت] ' . $transcribed;
                        logMessage('Audio transcribed successfully', 'INFO', $from);
                    } else {
                        logMessage('Audio transcription failed', 'WARNING', $from);
                    }
                }
            }
        }

        // Image
        elseif ($msgType === 'image') {
            $mediaId = $message['image']['id'] ?? '';
            if ($mediaId) {
                $download = downloadWhatsAppMedia($mediaId);
                if ($download && !empty($download['path'])) {
                    $incomingMediaMeta['media_files'][] = [
                        'type' => 'image',
                        'path' => $download['path'],
                        'mime' => $download['mime'] ?? ''
                    ];
                    logMessage('Image downloaded: ' . $download['path'], 'INFO', $from);
                }
            }
        }

        // Video (optional: extract a frame if ffmpeg available)
        elseif ($msgType === 'video') {
            $mediaId = $message['video']['id'] ?? '';
            if ($mediaId) {
                $download = downloadWhatsAppMedia($mediaId);
                if ($download && !empty($download['path'])) {
                    $incomingMediaMeta['media_files'][] = [
                        'type' => 'video',
                        'path' => $download['path'],
                        'mime' => $download['mime'] ?? ''
                    ];
                    $frame = extractVideoFrame($download['path']);
                    if ($frame) {
                        $incomingMediaMeta['media_files'][] = [
                            'type' => 'image',
                            'path' => $frame,
                            'mime' => 'image/jpeg',
                            'note' => 'extracted_video_frame'
                        ];
                    }
                    logMessage('Video downloaded: ' . $download['path'], 'INFO', $from);
                }
            }
        }
    }

    // Messenger / Instagram attachments (download by URL when available)
    if (in_array($platform, ['messenger','instagram'], true)) {
        $msgType = $message['type'] ?? 'text';
        $url = $message['url'] ?? '';
        if ($url && in_array($msgType, ['image','video','audio','document'], true)) {
            $download = downloadExternalMedia($url, $msgType);
            if ($download && !empty($download['path'])) {
                $incomingMediaMeta['media_files'][] = [
                    'type' => $msgType,
                    'path' => $download['path'],
                    'mime' => $download['mime'] ?? ''
                ];

                if ($msgType === 'audio') {
                    $transcribed = transcribeAudioOpenAI($download['path']);
                    if ($transcribed) {
                        $userInput = '[صوت] ' . $transcribed;
                        logMessage('Audio transcribed successfully', 'INFO', $from);
                    }
                } elseif ($msgType === 'video') {
                    $frame = extractVideoFrame($download['path']);
                    if ($frame) {
                        $incomingMediaMeta['media_files'][] = [
                            'type' => 'image',
                            'path' => $frame,
                            'mime' => 'image/jpeg',
                            'note' => 'extracted_video_frame'
                        ];
                    }
                }
            } elseif ($msgType === 'image') {
                // Keep URL fallback so previews are still forwarded even if download fails.
                $incomingMediaMeta['media_files'][] = [
                    'type' => 'image',
                    'path' => '',
                    'mime' => '',
                    'url'  => $url,
                    'note' => 'external_url_only'
                ];
                logMessage('External image download failed, using URL fallback for admin forwarding', 'WARNING', $from);
            }
        }
    }
} catch (Throwable $e) {
    logMessage('Media handling error: ' . $e->getMessage(), 'ERROR', $from);
}


// ======== تحويل رسائل فريق المبيعات (اختياري) ========
// ⚠️ افتراضيًا مقفول لتجنب الإزعاج. فعّل من config.php لو محتاجه.
if (($config['enable_internal_team_forwarding'] ?? false) && $from && !empty($forwardNumbers) && in_array($from, $forwardNumbers, true)) {
    $recipients = array_unique(array_merge([$targetNumber], $forwardNumbers));
    foreach ($recipients as $rcpt) {
        if ($rcpt === $from || !$rcpt) continue; // don't notify sender
        sendInternalAlert($rcpt, "رسالة داخلية من فريق المبيعات من $from:\n$userInput");
    }
    // Extra emphasis to Sales Manager
    $manager = $config['sales_manager_number'] ?? '';
    if ($manager && $manager !== $from) {
        sendInternalAlert($manager, "[تنبيه مدير المبيعات] رسالة من $from:\n$userInput");
    }
}

logMessage("Received: '" . mb_substr($userInput, 0, 100) . "'", 'INFO', $identityKey);

    // Rate limit & duplicate
    if (!$from || !checkRateLimit($identityKey) || isDuplicateMessage($identityKey, $userInput)) {
        http_response_code(200);
        exit;
    }

    // تجهيز بيانات إضافية لكل رسالة
$meta = [
    'type' => $message['type'] ?? 'unknown',
    'raw' => $message,
    'platform' => $platform,
    'source_sender_id' => $from,
    'customer_id' => $identityKey,
];
if (!empty($identity['wa_phone'])) {
    $meta['wa_phone'] = (string)$identity['wa_phone'];
}

// Merge any downloaded media meta (paths, extracted frames, etc.)
if (!empty($incomingMediaMeta) && is_array($incomingMediaMeta)) {
    $meta = array_merge($meta, $incomingMediaMeta);
}

// تخزين ID الميديا لو فيه
foreach (['image', 'video', 'audio', 'document', 'sticker'] as $mtype) {
    if (isset($message[$mtype]['id'])) {
        $meta['media_id'] = $message[$mtype]['id'];
        $meta['caption'] = $message[$mtype]['caption'] ?? '';
    }
}

// استخراج الأكواد الرقمية (OTP)
if (preg_match('/\b\d{4,8}\b/', normalizeDigitsToAscii($userInput), $otpMatch)) {
    $meta['otp'] = $otpMatch[0];
}

$hasIncomingMedia = (!empty($incomingMediaMeta['media_files']) || in_array(($message['type'] ?? ''), ['image','video','audio','document'], true));

// Detect if this is the first-ever message from this customer
    $isNewConversation = empty(loadConversation($identityKey));

    // Load customer/chat, add message
    $customer = loadCustomerData($identityKey);
    $customer['phone'] = $identityKey;
    if (!empty($identity['wa_phone'])) {
        $customer['wa_phone'] = (string)$identity['wa_phone'];
        $customer['contact_phone'] = (string)$identity['wa_phone'];
    }
    if (empty($customer['name']) && !empty($message['contact_name'])) {
        $customer['name'] = mb_substr(trim((string)$message['contact_name']), 0, 60, 'UTF-8');
    }
    if ($platform === 'messenger') {
        $customer['messenger_psid'] = $from;
    } elseif ($platform === 'instagram') {
        $customer['instagram_id'] = $from;
    }

    $addedPreviews = [];
    if (!empty($incomingMediaMeta['media_files']) && is_array($incomingMediaMeta['media_files'])) {
        $addedPreviews = registerIncomingPreviewMedia(
            $identityKey,
            $platform,
            (string)$from,
            $customer,
            $incomingMediaMeta['media_files']
        );
        if (!empty($addedPreviews)) {
            $meta['preview_links'] = array_values(array_filter(array_map(
                fn($p) => (string)($p['url'] ?? ''),
                $addedPreviews
            )));
        }
    }

    addToChatLog($identityKey, $userInput, 'user', $meta);
    updateCustomerProfile($customer, $userInput);
    $salesUpdates = updateSalesProfileFromInput($customer, $userInput, $platform);
    $reportableSalesUpdates = array_values(array_filter($salesUpdates, function ($field) {
        return !in_array($field, ['sales_flow_started_at', 'sales_last_question_field', 'sales_last_question_at'], true);
    }));
    if (!empty($reportableSalesUpdates)) {
        $salesSummary = buildSalesSummaryLine($customer);
        if ($salesSummary !== '') {
            addToChatLog($identityKey, "📋 تحديث بيانات المبيعات: {$salesSummary}", 'system', [
                'event' => 'sales_data_updated',
                'fields' => $reportableSalesUpdates,
                'platform' => $platform,
                'source_sender_id' => $from,
            ]);
        }
    }
    if (!empty($addedPreviews)) {
        addToChatLog($identityKey, buildPreviewTimelineEventText($addedPreviews), 'system', [
            'event' => 'preview_saved',
            'count' => count($addedPreviews),
            'preview_links' => array_values(array_filter(array_map(
                fn($p) => (string)($p['url'] ?? ''),
                $addedPreviews
            ))),
            'platform' => $platform,
            'source_sender_id' => $from,
        ]);
    }

    // Messenger flow: optional WhatsApp number capture (non-blocking)
    if (($identity['needs_wa_phone'] ?? false) && $platform === 'messenger') {
        $customer['wa_phone_pending'] = true;
        if (empty($customer['wa_phone_pending_since'])) {
            $customer['wa_phone_pending_since'] = time();
        }

        if (shouldSendWaPhoneCapturePrompt($customer)) {
            $isEnglish = isEnglishOnlyInput($userInput);
            $prompt = buildWaPhoneCapturePrompt($isEnglish);
            $quickReplies = buildWaPhoneCaptureQuickReplies($isEnglish);
            $sent = sendMessengerQuickReplies($from, $prompt, $quickReplies);

            if ($sent) {
                addToChatLog($identityKey, $prompt, 'assistant', [
                    'system_flow' => 'wa_phone_capture_soft',
                    'customer_id' => $identityKey,
                ]);
                $customer['wa_phone_prompt_last_sent'] = time();
                $customer['wa_phone_prompt_count'] = (int)($customer['wa_phone_prompt_count'] ?? 0) + 1;
                logMessage('Sent non-blocking WhatsApp capture prompt on Messenger', 'INFO', $identityKey);
            } else {
                logMessage('Failed to send Messenger quick-reply WA capture prompt', 'WARNING', $identityKey);
            }
        }
    }

    if (($identity['linked_now'] ?? false) && in_array($platform, ['messenger', 'instagram'], true)) {
        $customer['wa_phone'] = (string)($identity['wa_phone'] ?? '');
        $customer['contact_phone'] = (string)($identity['wa_phone'] ?? '');
        $customer['wa_phone_pending'] = false;
        unset($customer['wa_phone_pending_since']);
        updateSalesProfileFromInput($customer, $userInput, $platform);

        $ack = buildWaPhoneCapturedAck(isEnglishOnlyInput($userInput));
        if ($platform === 'messenger') {
            $ack = ensureMessengerSalesFollowup($ack, $customer, $platform, $userInput, true);
        }
        $sent = ($platform === 'messenger')
            ? sendMessengerMessage($from, $ack)
            : sendInstagramDM($from, $ack, $config['facebook_token']);
        if (!$sent) {
            throw new Exception('Failed to send phone-link acknowledgment');
        }

        addToChatLog($identityKey, $ack, 'assistant', [
            'system_flow' => 'wa_phone_linked',
            'customer_id' => $identityKey,
            'wa_phone' => (string)($identity['wa_phone'] ?? ''),
        ]);
        saveCustomerData($identityKey, $customer);

        try {
            notifyAdminOnInteraction([
                'event'      => 'message',
                'platform'   => $platform,
                'from'       => (string)$from,
                'customer_id'=> (string)$identityKey,
                'wa_phone'   => (string)($identity['wa_phone'] ?? ''),
                'from_name'  => (string)($customer['name'] ?? ($message['contact_name'] ?? '')),
                'text'       => "تم ربط رقم واتساب العميل: " . (string)($identity['wa_phone'] ?? ''),
                'has_media'  => $hasIncomingMedia,
                'customer'   => [
                    'city' => (string)($customer['city'] ?? ''),
                    'place_type' => (string)($customer['place_type'] ?? ''),
                    'services' => customerServicesText($customer),
                    'project_scope' => (string)($customer['project_scope'] ?? ''),
                    'budget_range' => (string)($customer['budget_range'] ?? ''),
                    'project_timeline' => (string)($customer['project_timeline'] ?? ''),
                ],
                'timestamp'  => time(),
            ]);
        } catch (Throwable $e) {
            logMessage("Admin notify error: " . $e->getMessage(), 'WARNING', $identityKey);
        }

        try {
            if (!empty($incomingMediaMeta['media_files'])) {
                forwardIncomingMediaToAdmin($incomingMediaMeta['media_files'], [
                    'platform'  => $platform,
                    'from'      => (string)$from,
                    'customer_id' => (string)$identityKey,
                    'wa_phone'  => (string)($identity['wa_phone'] ?? ''),
                    'from_name' => (string)($customer['name'] ?? ($message['contact_name'] ?? '')),
                    'text'      => (string)$userInput,
                    'intent'    => 'wa_phone_linked',
                    'customer'  => [
                        'city'       => (string)($customer['city'] ?? ''),
                        'place_type' => (string)($customer['place_type'] ?? ''),
                        'services'   => customerServicesText($customer),
                        'project_scope' => (string)($customer['project_scope'] ?? ''),
                        'budget_range' => (string)($customer['budget_range'] ?? ''),
                        'project_timeline' => (string)($customer['project_timeline'] ?? ''),
                    ],
                    'timestamp' => time(),
                ]);
            }
        } catch (Throwable $e) {
            logMessage("Media forward error: " . $e->getMessage(), 'WARNING', $identityKey);
        }

        logMessage('Messenger identity linked to WhatsApp number', 'INFO', $identityKey);
        http_response_code(200);
        echo json_encode(['status' => 'wa_number_linked', 'customer_id' => $identityKey]);
        exit;
    }

    if (!empty($identity['wa_phone'])) {
        $customer['wa_phone_pending'] = false;
        unset($customer['wa_phone_pending_since']);
    } elseif (($identity['needs_wa_phone'] ?? false) && $platform === 'messenger') {
        $customer['wa_phone_pending'] = true;
    }

    $detectedIntent = detectUserIntent($userInput, $customer, !empty($incomingMediaMeta['media_files']));
    $customer['last_detected_intent'] = $detectedIntent;

    // Build conversation for AI
    $conversation = loadConversation($identityKey);
    $conversation = initConversation($conversation, $customer);
    $conversation = trimConversationHistory($conversation);
    if ($isNewConversation) {
        $firstReplyHint = "هذه أول رسالة من عميل جديد. ابدأ بترحيب قصير ثم اسأل عن المشكلة أو الطلب قبل تقديم التفاصيل.";
        $insertAt = (!empty($conversation) && ($conversation[0]['role'] ?? '') === 'system') ? 1 : 0;
        array_splice($conversation, $insertAt, 0, [[
            'role' => 'system',
            'content' => $firstReplyHint
        ]]);
    }

    // ===== Get AI Response (with ultra-fast intent fallback) =====
    $aiResponse = getFastIntentReply($userInput, [
        'platform'   => $platform,
        'has_media'  => !empty($incomingMediaMeta['media_files']),
        'from_name'  => (string)($customer['name'] ?? ($message['contact_name'] ?? '')),
    ]);
    if ($aiResponse !== null) {
        logMessage("FAST INTENT used for {$platform}", 'INFO', $identityKey);
    } else {
        $aiResponse = getAIResponse($conversation);
    }
    if ($aiResponse === null) {
        $aiResponse = 'عذراً، حدث خطأ تقني. سيتم التواصل معك قريباً من فريق برادايس جروب. للاستفسار الفوري: +20 10 94945225';
    }
    logMessage("AI RAW: " . $aiResponse, 'INFO', $identityKey);

    // ===== Hard guardrail: NEVER send pricing =====
    $aiResponse = enforceNoPricing($aiResponse, $userInput);


        // ===== No buttons/quick replies (text only) =====
    $responseText = stripButtonsMarkup($aiResponse);

    // Add preview instruction only after intent becomes clear.
    $responseText = ensurePreviewInstruction($responseText, $customer, $detectedIntent);
    $responseText = ensureFirstReplyGreeting($responseText, $isNewConversation, $customer, $detectedIntent, $userInput);
    $responseText = ensureMessengerSalesFollowup($responseText, $customer, $platform, $userInput);

    // ===== Send Actual Message (text only across all channels) =====
    $messageSent = false;
    $platform = $message['platform'] ?? 'whatsapp';

    if ($platform === 'messenger') {
        logMessage("Messenger response to $from", 'INFO', $identityKey);
        $messageSent = sendMessengerMessage($from, $responseText);
    } elseif ($platform === 'instagram') {
        logMessage("Instagram DM response to $from", 'INFO', $identityKey);
        $messageSent = sendInstagramDM($from, $responseText, $config['facebook_token']);
    } else {
        logMessage("WhatsApp text response to $from", 'INFO', $identityKey);
        $messageSent = sendWhatsAppMessage($from, $responseText);
    }

    if (!$messageSent) {
        throw new Exception('Failed to send outbound message');
    }

    addToChatLog($identityKey, $responseText, 'assistant');
    saveCustomerData($identityKey, $customer);
    trackConversionSignals($identityKey, $userInput, $aiResponse);

    // ======== Post-reply tasks (non-blocking from customer perspective) ========
    try {
        notifyAdminOnInteraction([
            'event'      => 'message',
            'platform'   => $platform ?? 'whatsapp',
            'from'       => (string)$from,
            'customer_id'=> (string)$identityKey,
            'wa_phone'   => (string)($identity['wa_phone'] ?? ''),
            'from_name'  => (string)($customer['name'] ?? ($message['contact_name'] ?? '')),
            'text'       => (string)$userInput,
            'has_media'  => (!empty($incomingMediaMeta['media_files']) || in_array(($message['type'] ?? ''), ['image','video','audio','document'], true)),
            'customer'   => [
                'city' => (string)($customer['city'] ?? ''),
                'place_type' => (string)($customer['place_type'] ?? ''),
                'services' => customerServicesText($customer),
                'project_scope' => (string)($customer['project_scope'] ?? ''),
                'budget_range' => (string)($customer['budget_range'] ?? ''),
                'project_timeline' => (string)($customer['project_timeline'] ?? ''),
            ],
            'timestamp'  => time(),
        ]);
    } catch (Throwable $e) {
        logMessage("Admin notify error: " . $e->getMessage(), 'WARNING', $identityKey);
    }

    try {
        if (!empty($incomingMediaMeta['media_files'])) {
            forwardIncomingMediaToAdmin($incomingMediaMeta['media_files'], [
                'platform'  => $platform ?? 'whatsapp',
                'from'      => (string)$from,
                'customer_id' => (string)$identityKey,
                'wa_phone'  => (string)($identity['wa_phone'] ?? ''),
                'from_name' => (string)($customer['name'] ?? ($message['contact_name'] ?? '')),
                'text'      => (string)$userInput,
                'intent'    => (string)$detectedIntent,
                'customer'  => [
                    'city'       => (string)($customer['city'] ?? ''),
                    'place_type' => (string)($customer['place_type'] ?? ''),
                    'services'   => customerServicesText($customer),
                    'project_scope' => (string)($customer['project_scope'] ?? ''),
                    'budget_range' => (string)($customer['budget_range'] ?? ''),
                    'project_timeline' => (string)($customer['project_timeline'] ?? ''),
                ],
                'timestamp' => time(),
            ]);
        }
    } catch (Throwable $e) {
        logMessage("Media forward error: " . $e->getMessage(), 'WARNING', $identityKey);
    }

    if ($isNewConversation && ($config['notify_on_new_chat'] ?? false)) {
        try {
            notifySalesOnNewChat($identityKey, $platform ?? 'whatsapp', $customer);
        } catch (Throwable $e) {
            logMessage('Failed to notify sales on new chat: ' . $e->getMessage(), 'WARNING', $identityKey);
        }
    }

    logMessage('Reply sent successfully', 'INFO', $identityKey);
    http_response_code(200);
    echo json_encode(['status' => 'success']);

} catch (Throwable $e) {
    handleError($e, $from ?? null, $userInput ?? null);
}
}








// ======== Core Functions ========
function handleVerification(): void {
    global $config;

    // Meta standard params: hub.mode / hub.verify_token / hub.challenge
    // Keep backward compatibility with legacy underscore params.
    $mode = (string)($_GET['hub.mode'] ?? $_GET['hub_mode'] ?? '');
    $verifyToken = (string)($_GET['hub.verify_token'] ?? $_GET['hub_verify_token'] ?? '');
    $challenge = (string)($_GET['hub.challenge'] ?? $_GET['hub_challenge'] ?? '');

    if ($verifyToken === '' || $challenge === '') {
        logMessage('Webhook verify request missing token or challenge', 'WARNING');
        http_response_code(400);
        echo json_encode(['error' => 'Bad Request']);
        exit;
    }

    if ($mode !== '' && $mode !== 'subscribe') {
        logMessage("Webhook verify invalid mode: {$mode}", 'WARNING');
        http_response_code(400);
        echo json_encode(['error' => 'Invalid mode']);
        return;
    }

    if (hash_equals((string)$config['verify_token'], $verifyToken)) {
        logMessage('Webhook verified');
        header('Content-Type: text/plain; charset=utf-8');
        echo $challenge;
    } else {
        logMessage('Invalid verify token', 'ERROR');
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
    }
}





function extractWebhookMessage(array $data): ?array {
    
        $webhookHash = md5(json_encode($data));
    $webhookCacheFile = "cache/webhook_$webhookHash.txt";
    
    if (file_exists($webhookCacheFile)) {
        $lastProcessed = (int)file_get_contents($webhookCacheFile);
        if ((time() - $lastProcessed) < 30) { // 30 ثانية
            return null; // تجاهل الـ webhook المكرر
        }
    }
    
    // WhatsApp payload
    if (isset($data['entry'][0]['changes'][0]['value']['messages'][0])) {
        $msg = $data['entry'][0]['changes'][0]['value']['messages'][0];
        $msg['platform'] = 'whatsapp';
        // Add contact name when available (for admin notifications)
        $contact = $data['entry'][0]['changes'][0]['value']['contacts'][0]['profile']['name'] ?? null;
        if ($contact) { $msg['contact_name'] = $contact; }
        return $msg;
    }
    
        // Messenger / Instagram DMs payload (supports attachments)
    elseif (isset($data['entry'][0]['messaging'][0])) {
        $msgObj = $data['entry'][0]['messaging'][0];

        // Ignore delivery/read receipts
        if (isset($msgObj['delivery']) || isset($msgObj['read'])) {
            return null;
        }

        $object = $data['object'] ?? 'page';
        $platform = ($object === 'instagram') ? 'instagram' : 'messenger';

        $from = $msgObj['sender']['id'] ?? '';
        $msg = $msgObj['message'] ?? [];

        // Text message
        if (!empty($msg['text'])) {
            return [
                'from'     => $from,
                'type'     => 'text',
                'platform' => $platform,
                'text'     => $msg['text']
            ];
        }

        // Attachments (images/videos/audio/files)
        if (!empty($msg['attachments']) && is_array($msg['attachments'])) {
            $att0 = $msg['attachments'][0] ?? [];
            $attType = $att0['type'] ?? 'file';
            $url = $att0['payload']['url'] ?? '';

            $mappedType = 'document';
            if ($attType === 'image') $mappedType = 'image';
            elseif ($attType === 'video') $mappedType = 'video';
            elseif ($attType === 'audio') $mappedType = 'audio';

            return [
                'from'        => $from,
                'type'        => $mappedType,
                'platform'    => $platform,
                'text'        => $msg['text'] ?? '',
                'attachments' => $msg['attachments'],
                'url'         => $url
            ];
        }

        return null;
    }

    // Facebook/Instagram Comments (iterate all entry/changes; strict filtering only)
    if (!empty($data['entry']) && is_array($data['entry'])) {
        foreach ($data['entry'] as $entryIndex => $entry) {
            if (!is_array($entry)) continue;
            $changes = (isset($entry['changes']) && is_array($entry['changes'])) ? $entry['changes'] : [];
            foreach ($changes as $changeIndex => $change) {
                if (!is_array($change)) continue;
                $field = (string)($change['field'] ?? '');
                $value = (isset($change['value']) && is_array($change['value'])) ? $change['value'] : [];

                file_put_contents(
                    'logs/comment_debug.log',
                    date('Y-m-d H:i:s') . " WEBHOOK RECEIVED\n" .
                    "ENTRY_IDX: {$entryIndex}\n" .
                    "CHANGE_IDX: {$changeIndex}\n" .
                    "FIELD: {$field}\n" .
                    "ENTRY_ID: " . (string)($entry['id'] ?? '') . "\n" .
                    "VALUE: " . json_encode($value, JSON_UNESCAPED_UNICODE) . "\n\n",
                    FILE_APPEND
                );

                $commentEvent = extractCommentEventFromChange($entry, $change);
                if ($commentEvent !== null) {
                    @file_put_contents($webhookCacheFile, (string)time(), LOCK_EX);
                    return $commentEvent;
                }
            }
        }
    }

    // تجاهل delivery receipts بدلاً من log كـ unhandled
    if (isset($data['entry'][0]['messaging'][0]['delivery']) || isset($data['entry'][0]['messaging'][0]['read'])) {
        return null;
    }

    // Log للحالات غير المتوقعة فقط
    file_put_contents('logs/webhook_unhandled.log', 
        date('Y-m-d H:i:s') . " UNHANDLED WEBHOOK: " . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n", 
        FILE_APPEND
    );
    return null;
}



function mentionUser($postId, $userId, $accessToken) {
    if (empty($postId) || empty($userId) || empty($accessToken)) {
        return false;
    }
    
    // محاولة عمل mention في comment منفصل
    $url = "https://graph.facebook.com/v19.0/{$postId}/comments";
    $data = [
        'message' => "شكرًا لتفاعلك معنا! 🙏 @[$userId]",
        'access_token' => $accessToken
    ];
    
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($data),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_TIMEOUT => 15
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    // Log محاولة المنشن
    file_put_contents('logs/mention_attempts.log', 
        date('Y-m-d H:i:s') . " Mention attempt: Post $postId, User $userId, HTTP: $httpCode\n" .
        "Response: $response\n\n", 
        FILE_APPEND
    );
    
    curl_close($ch);
    
    return $httpCode >= 200 && $httpCode < 300;
}









function extractMessageText(array $message): string {
    

    if (($message['type'] ?? '') === 'comment') {
        $txt = trim($message['comment_text'] ?? '');
        return $txt === '' ? '[تعليق فارغ]' : $txt;
    }

    if (in_array(($message['platform'] ?? ''), ['messenger','instagram'], true)) {
        $ptype = $message['type'] ?? 'text';
        $txt = trim((string)($message['text'] ?? ''));
        if ($ptype === 'text') {
            return $txt !== '' ? $txt : '[رسالة فارغة]';
        }
        // Attachments on Messenger/Instagram
        if ($ptype === 'image') return $txt !== '' ? "[صورة] $txt" : "[صورة]";
        if ($ptype === 'video') return $txt !== '' ? "[فيديو] $txt" : "[فيديو]";
        if ($ptype === 'audio') return $txt !== '' ? "[صوت] $txt" : "[صوت]";
        if ($ptype === 'document') return $txt !== '' ? "[ملف] $txt" : "[ملف]";
        return $txt !== '' ? $txt : "[رسالة]";
    }
    
    $type = $message['type'] ?? 'unknown';

    switch($type) {
        case 'text':
            $text = $message['text']['body'] ?? '';
            break;

        case 'button':
            $text = $message['button']['payload'] ?? $message['button']['text'] ?? '[زر تفاعلي]';
            break;

        case 'interactive':
            $text = extractInteractiveMessage($message['interactive'] ?? []);
            break;

        case 'image':
            $caption = $message['image']['caption'] ?? '';
            $media_id = $message['image']['id'] ?? '';
            $text = "[صورة] ID: $media_id" . ($caption ? " - $caption" : '');
            break;

        case 'video':
            $caption = $message['video']['caption'] ?? '';
            $media_id = $message['video']['id'] ?? '';
            $text = "[فيديو] ID: $media_id" . ($caption ? " - $caption" : '');
            break;

        case 'audio':
        case 'voice':
            $media_id = $message[$type]['id'] ?? '';
            $text = "[صوت] ID: $media_id";
            break;

        case 'document':
            $caption = $message['document']['caption'] ?? '';
            $media_id = $message['document']['id'] ?? '';
            $filename = $message['document']['filename'] ?? '';
            $text = "[ملف] $filename - ID: $media_id" . ($caption ? " - $caption" : '');
            break;

        case 'sticker':
            $media_id = $message['sticker']['id'] ?? '';
            $text = "[ملصق] ID: $media_id";
            break;

        case 'location':
            $lat = $message['location']['latitude'] ?? '';
            $lng = $message['location']['longitude'] ?? '';
            $name = $message['location']['name'] ?? '';
            $address = $message['location']['address'] ?? '';
            $text = "[موقع جغرافي] $name $address (Lat: $lat, Lng: $lng)";
            break;

        case 'contacts':
            $contacts = $message['contacts'] ?? [];
            $text = "[جهة اتصال] " . json_encode($contacts, JSON_UNESCAPED_UNICODE);
            break;

        case 'order':
            $order = $message['order'] ?? [];
            $text = "[طلب منتج] " . json_encode($order, JSON_UNESCAPED_UNICODE);
            break;

default:
        logMessage('UNSUPPORTED MESSAGE: ' . json_encode($message, JSON_UNESCAPED_UNICODE), 'ERROR');
    // لو الرسالة فيها أي نص مهما كان المفتاح، حتى لو النوع "unsupported" أو غريب
    $fallback = '';
    foreach (['body', 'caption', 'payload', 'text', 'message', 'value'] as $key) {
        if (!empty($message[$type][$key])) {
            $fallback = $message[$type][$key];
            break;
        }
    }
    // أحيانًا الـ payload نفسه هو نص
    if (empty($fallback) && !empty($message[$type]) && is_string($message[$type])) {
        $fallback = $message[$type];
    }
    // لو وجد نص، اعرضه مباشرة (بدون أي قوس أو تعليق)
    if (!empty($fallback)) {
        $text = $fallback;
    } else {
        $details = isset($message[$type]) ? json_encode($message[$type], JSON_UNESCAPED_UNICODE) : '';
        $text = "[نوع رسالة: $type]" . ($details ? ' - ' . $details : '');
    }


    }

    // Handle replies
    if (isset($message['context']['from'])) {
        $text = "رد على رسالة: " . $text;
    }

    $text = trim(strip_tags($text));
    if (empty($text)) $text = "[رسالة فارغة]";

    return mb_substr($text, 0, 1500, 'UTF-8');
}


function sendMessengerMessage(string $to, string $message): bool {
    global $config;
    $token = trim((string)($config['messenger_token'] ?? ''));
    if ($to === '' || $token === '') {
        logMessage('Messenger send skipped: missing recipient or token', 'WARNING', $to);
        return false;
    }

    $apiVersion = $config['meta_api_version'] ?? 'v19.0';
    $payload = json_encode([
        'recipient' => ['id' => $to],
        'message'   => ['text' => $message]
    ], JSON_UNESCAPED_UNICODE);

    $ch = curl_init("https://graph.facebook.com/{$apiVersion}/me/messages?access_token=" . urlencode($token));
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['messenger_timeout'] ?? 8,
        CURLOPT_SSL_VERIFYPEER => true
    ]);
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    logMessage("Messenger API response: $httpCode $response", 'INFO', $to);

    curl_close($ch);
    return $httpCode >= 200 && $httpCode < 300;
}

function sendMessengerQuickReplies(string $to, string $message, array $quickReplies): bool {
    global $config;
    $token = trim((string)($config['messenger_token'] ?? ''));
    if ($to === '' || $token === '') {
        logMessage('Messenger quick reply skipped: missing recipient or token', 'WARNING', $to);
        return false;
    }

    $prepared = [];
    foreach (array_slice($quickReplies, 0, 3) as $qr) {
        $title = '';
        $payload = '';
        if (is_array($qr)) {
            $title = trim((string)($qr['title'] ?? ''));
            $payload = trim((string)($qr['payload'] ?? ''));
        } else {
            $title = trim((string)$qr);
        }
        if ($title === '') continue;
        if ($payload === '') $payload = mb_strtolower(str_replace(' ', '_', $title), 'UTF-8');

        $prepared[] = [
            'content_type' => 'text',
            'title' => mb_substr($title, 0, 20, 'UTF-8'),
            'payload' => mb_substr($payload, 0, 100, 'UTF-8'),
        ];
    }

    if (empty($prepared)) {
        return sendMessengerMessage($to, $message);
    }

    $apiVersion = $config['meta_api_version'] ?? 'v19.0';
    $payload = json_encode([
        'recipient' => ['id' => $to],
        'messaging_type' => 'RESPONSE',
        'message'   => [
            'text' => $message,
            'quick_replies' => $prepared
        ]
    ], JSON_UNESCAPED_UNICODE);

    $ch = curl_init("https://graph.facebook.com/{$apiVersion}/me/messages?access_token=" . urlencode($token));
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['messenger_timeout'] ?? 8,
        CURLOPT_SSL_VERIFYPEER => true
    ]);
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    logMessage("Messenger quick replies response: $httpCode $response", 'INFO', $to);
    curl_close($ch);

    return $httpCode >= 200 && $httpCode < 300;
}


function extractInteractiveMessage(array $interactive): string {
    if ($interactive['button_reply']['title'] ?? false) {
        return $interactive['button_reply']['title'];
    }
    if ($interactive['list_reply']['title'] ?? false) {
        return $interactive['list_reply']['title'];
    }
    if ($interactive['product']['title'] ?? false) {
        return '[استفسار عن منتج: ' . $interactive['product']['title'] . ']';
    }
    return '[رسالة تفاعلية]';
}

function checkRateLimit(string $phone): bool {
    global $config;
    $rateFile = "logs/rate_$phone.txt";
    $now = time();

    if (file_exists($rateFile)) {
        $lastRequest = (int)@file_get_contents($rateFile);
        if ($now - $lastRequest < $config['rate_limit_seconds']) {
            logMessage("Rate limited", 'RATE_LIMIT', $phone);
            addToChatLog($phone, 'تم تجاهل الرسالة بسبب الإرسال السريع', 'system');
            return false;
        }
    }

    @file_put_contents($rateFile, $now);
    return true;
}

function isDuplicateMessage(string $phone, string $message): bool {
    global $config;
    $conversation = loadConversation($phone);
    $now = time();

    foreach (array_slice($conversation, -5) as $msg) {
        if (
            $msg['role'] === 'user' &&
            $msg['content'] === $message &&
            isset($msg['timestamp']) &&
            ($now - $msg['timestamp']) < $config['duplicate_threshold']
        ) {
            logMessage("Duplicate message", 'WARNING', $phone);
            return true;
        }
    }
    return false;
}

function initConversation(array $conversation, array $customer): array {
    global $config;
    if (empty($conversation) || $conversation[0]['role'] !== 'system') {
        $systemPrompt = buildSystemPrompt($customer);
        array_unshift($conversation, ['role' => 'system', 'content' => $systemPrompt]);
    }
    return $conversation;
}

function trimConversationHistory(array $conversation): array {
    global $config;
    if (count($conversation) > $config['max_history']) {
        $systemMsg = array_shift($conversation);
        $conversation = array_slice($conversation, -($config['max_history'] - 1));
        array_unshift($conversation, $systemMsg);
    }
    return $conversation;
}

function normalizeIntentInput(string $text): string {
    $text = trim(mb_strtolower($text, 'UTF-8'));
    if ($text === '') return '';

    $text = strtr($text, [
        'أ' => 'ا',
        'إ' => 'ا',
        'آ' => 'ا',
        'ؤ' => 'و',
        'ئ' => 'ي',
        'ى' => 'ي',
    ]);
    $text = preg_replace('/[^\p{L}\p{N}\s]/u', ' ', $text);
    $text = preg_replace('/\s+/u', ' ', $text);
    return trim($text);
}

function containsAnyPhrase(string $normalizedText, array $phrases): bool {
    foreach ($phrases as $p) {
        $kw = normalizeIntentInput((string)$p);
        if ($kw === '') continue;
        if (mb_strpos($normalizedText, $kw) !== false) {
            return true;
        }
    }
    return false;
}

function isEnglishOnlyInput(string $text): bool {
    return (bool)(preg_match('/[A-Za-z]/', $text) && !preg_match('/[\x{0600}-\x{06FF}]/u', $text));
}

function isActionableIntent(string $intent): bool {
    return in_array($intent, [
        'pricing',
        'contact',
        'site_visit',
        'maintenance',
        'pool',
        'water_features',
        'landscape',
        'finishing',
        'design',
        'preview_media',
        'service_from_profile',
    ], true);
}

function detectUserIntent(string $userInput, array $customer = [], bool $hasMedia = false): string {
    $normalized = normalizeIntentInput($userInput);
    if ($hasMedia) return 'preview_media';
    if ($normalized === '' || $normalized === 'رساله فارغه') return 'unknown';

    if (containsAnyPhrase($normalized, ['شكرا', 'شكراً', 'thanks', 'thank you', 'thx'])) return 'thanks';
    if (containsAnyPhrase($normalized, ['اهلا', 'أهلا', 'السلام عليكم', 'صباح الخير', 'مساء الخير', 'hi', 'hello', 'hey'])) return 'greeting';
    if (containsAnyPhrase($normalized, ['سعر', 'اسعار', 'تكلفه', 'تكلفة', 'price', 'cost', 'quote', 'quotation', 'كام', 'كم'])) return 'pricing';
    if (containsAnyPhrase($normalized, ['اتصال', 'اتصل', 'كلمني', 'كلموني', 'رقم', 'واتساب', 'whatsapp', 'call', 'phone', 'contact'])) return 'contact';
    if (containsAnyPhrase($normalized, ['معاينه', 'معاينة', 'زياره', 'زيارة', 'موعد', 'visit', 'appointment', 'onsite'])) return 'site_visit';
    if (containsAnyPhrase($normalized, ['صيانه', 'صيانة', 'تسريب', 'فلتر', 'مضخه', 'مضخة', 'maintenance', 'repair'])) return 'maintenance';
    if (containsAnyPhrase($normalized, ['مسبح', 'حمام سباحه', 'حمام سباحة', 'pool', 'swimming'])) return 'pool';
    if (containsAnyPhrase($normalized, ['شلال', 'شلالات', 'نافوره', 'نافورة', 'fountain', 'waterfall', 'water feature'])) return 'water_features';
    if (containsAnyPhrase($normalized, ['لاندسكيب', 'حديقه', 'حديقة', 'landscape', 'garden'])) return 'landscape';
    if (containsAnyPhrase($normalized, ['تشطيب', 'تشطيبات', 'واجهات', 'finishing', 'renovation'])) return 'finishing';
    if (containsAnyPhrase($normalized, ['تصميم', 'design', '3d', '2d', 'مخطط', 'رسومات'])) return 'design';

    if (!empty($customer['services']) && is_array($customer['services'])) {
        return 'service_from_profile';
    }

    return 'unknown';
}

/**
 * Ultra-fast fallback replies for obvious intents to reduce latency on all platforms.
 * Returns null when intent is unclear so normal AI flow can continue.
 */
function getFastIntentReply(string $userInput, array $context = []): ?string {
    global $config;

    $normalized = normalizeIntentInput($userInput);
    if ($normalized === '' || $normalized === 'رساله فارغه') {
        return null;
    }

    $isEnglish = isEnglishOnlyInput($userInput);
    $hasMedia = (bool)($context['has_media'] ?? false);
    $shortMessage = mb_strlen($normalized, 'UTF-8') <= 32;

    if ($hasMedia) {
        return $isEnglish
            ? "Preview received successfully. Our team will review it now. Is this a new build or maintenance?"
            : "تم استلام المعاينة بنجاح ✅ فريقنا هيراجعها الآن. تحبها تنفيذ جديد ولا صيانة؟";
    }

    $exactQuickMap = [
        'شكرا' => 'شكرًا لحضرتك. هل تحب نرتب معاينة سريعة؟',
        'شكرا ليك' => 'تحت أمرك دائمًا. هل نكمل بخطوة المعاينة؟',
        'thanks' => 'Thanks. Would you like us to schedule a quick site visit?',
        'thank you' => 'You are welcome. Would you like to proceed with a quick site visit?',
        'hi' => 'Welcome to Paradise Group. Is your request for new execution or maintenance?',
        'hello' => 'Welcome to Paradise Group. Is your request for new execution or maintenance?',
    ];
    if (isset($exactQuickMap[$normalized])) {
        return $exactQuickMap[$normalized];
    }

    // Priority intents
    if (containsAnyPhrase($normalized, ['سعر', 'اسعار', 'تكلفه', 'تكلفة', 'price', 'cost', 'quote', 'quotation', 'كام', 'كم'])) {
        return $isEnglish
            ? "We can prepare an accurate quotation after location, size, and service type. Can you send city + dimensions or a site photo?"
            : "نقدر نجهز عرض دقيق بعد معرفة الموقع والمقاس ونوع الخدمة. ممكن تبعت المدينة + المقاسات أو صورة للمكان؟";
    }

    if (containsAnyPhrase($normalized, ['اتصال', 'اتصل', 'كلمني', 'كلموني', 'رقم', 'واتساب', 'whatsapp', 'call', 'phone', 'contact'])) {
        $previewNumber = (string)($config['preview_number_display'] ?? '+20 10 94945225');
        return $isEnglish
            ? "Sure, our team can contact you right away. Do you prefer a call or WhatsApp on {$previewNumber}?"
            : "أكيد، فريقنا يقدر يتواصل معك فورًا. تفضّل مكالمة ولا واتساب على {$previewNumber}؟";
    }

    if (containsAnyPhrase($normalized, ['معاينه', 'معاينة', 'زياره', 'زيارة', 'موعد', 'visit', 'appointment', 'onsite'])) {
        return $isEnglish
            ? "Great, we can arrange a quick site visit. What city/area is the project in?"
            : "ممتاز، نقدر نرتب معاينة سريعة. المشروع في أي مدينة/منطقة؟";
    }

    // For service-related requests, rely on website-grounded AI response.

    if ($shortMessage && containsAnyPhrase($normalized, ['شكرا', 'شكراً', 'thanks', 'thank you', 'thx'])) {
        return $isEnglish
            ? "You're welcome. Would you like us to schedule a quick visit?"
            : "العفو، تحت أمرك. تحب نرتب لك معاينة سريعة؟";
    }

    if ($shortMessage && containsAnyPhrase($normalized, ['اهلا', 'أهلا', 'السلام عليكم', 'صباح الخير', 'مساء الخير', 'hi', 'hello', 'hey'])) {
        return $isEnglish
            ? "Welcome to Paradise Group. Is your request for new execution or maintenance?"
            : "أهلاً وسهلاً بحضرتك في Paradise Group. محتاج تنفيذ جديد ولا صيانة؟";
    }

    return null;
}

// ======== Customer Management ========
function loadCustomerData(string $phone): array {
    $file = "customers/" . basename($phone) . ".json";
    if (file_exists($file)) {
        $data = json_decode(file_get_contents($file), true);
        return is_array($data) ? $data : getDefaultCustomerData($phone);
    }
    return getDefaultCustomerData($phone);
}

function getDefaultCustomerData(string $phone): array {
    return [
        'phone' => $phone,
        'wa_phone' => '',
        'contact_phone' => '',
        'name' => '',
        'city' => '',
        'place_type' => '',
        'services' => [],
        'project_city' => '',
        'project_place_type' => '',
        'project_services' => '',
        'project_scope' => '',
        'budget_range' => '',
        'project_timeline' => '',
        'lead_source' => '',
        'business_type' => '',
        'interests' => [],
        'conversation_count' => 0,
        'notes' => [],
        'qualification_status' => 'new',
        'lead_score' => 0,
        'created_at' => time(),
        'last_contact' => time(),
        'conversion_signals' => [],
        'previews' => [],
        'note' => '', // ملاحظة يدوي
        'custom_details' => '', // تفاصيل المشروع
        'followup_date' => null, // معاد الفولو اب (يونكس تايم أو null)
        'sales_flow_started_at' => 0,
        'sales_last_question_field' => '',
        'sales_last_question_at' => 0,
        'sales_flow_completed_at' => 0
    ];
}

function saveCustomerData(string $phone, array $data): void {
    $data['last_contact'] = time();
    $file = "customers/" . basename($phone) . ".json";
    @file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
}

function updateCustomerProfile(array &$customer, string $input): void {
    global $config;
    $input = $input ?? '';
    $customer['conversation_count']++;
    $customer['notes'][] = [
        'msg' => mb_substr($input, 0, 200),
        'ts' => time()
    ];

if (preg_match('/\b\d{4,8}\b/', $input, $otpMatch)) {
    if (!isset($customer['otps'])) $customer['otps'] = [];
    $customer['otps'][] = [
        'code' => $otpMatch[0],
        'ts' => time()
    ];
}

    if (count($customer['notes']) > $config['max_notes']) {
        $customer['notes'] = array_slice($customer['notes'], -$config['max_notes']);
    }

    $lower = mb_strtolower($input, 'UTF-8');

    // --- Lead slots (lightweight extraction) ---
    if (empty($customer['name'])) {
        if (preg_match('/(?:اسمي|انا|أنا)\s+([^\d\.\,\n]{2,40})/u', $input, $mm)) {
            $cand = trim($mm[1]);
            $cand = preg_replace('/\s+/', ' ', $cand);
            // Keep max 3 words
            $parts = preg_split('/\s+/', $cand);
            $cand = trim(implode(' ', array_slice($parts, 0, 3)));
            if (mb_strlen($cand, 'UTF-8') >= 2) {
                $customer['name'] = $cand;
            }
        }
    }

    if (empty($customer['city'])) {
        $cities = [
            'القاهرة','الجيزة','6 أكتوبر','اكتوبر','الشيخ زايد','زايد','التجمع','التجمع الخامس','مدينة نصر','المعادي',
            'مدينتي','الرحاب','الشروق','العبور',
            'الإسكندرية','اسكندرية','الساحل الشمالي','العين السخنة','الغردقة','شرم الشيخ','المنصورة','طنطا','الزقازيق',
            'الإسماعيلية','السويس','بورسعيد','دمياط','الفيوم','بني سويف','المنيا','أسيوط','سوهاج','قنا','الأقصر','أسوان'
        ];
        foreach ($cities as $c) {
            if (mb_strpos($lower, mb_strtolower($c, 'UTF-8')) !== false) {
                $customer['city'] = $c;
                break;
            }
        }
    }

    if (empty($customer['place_type'])) {
        $placeMap = [
            '/فيلا|villa/u' => 'فيلا',
            '/منزل|بيت|home/u' => 'منزل',
            '/فندق|hotel/u' => 'فندق',
            '/منتجع|resort/u' => 'منتجع',
            '/نادي|club/u' => 'نادي',
            '/شركة|office|commercial/u' => 'شركة/تجاري',
        ];
        foreach ($placeMap as $rgx => $val) {
            if (preg_match($rgx, $lower)) {
                $customer['place_type'] = $val;
                break;
            }
        }
    }

    // Detect service interests (best-effort)
    $serviceKw = [
        'حمامات السباحة' => ['مسبح','حمام سباحة','حمامات','pool','swimming'],
        'الشلالات والنافورات' => ['شلال','نافورة','نوافير','waterfall','fountain','water feature'],
        'اللاندسكيب' => ['لاندسكيب','تنسيق','حديقة','زراعة','ري','landscape','garden'],
        'التشطيبات' => ['تشطيب','واجهات','دهانات','رخام','حجر','finishing','renovation'],
        'الصيانة' => ['صيانة','maintenance','فلتر','مضخة','تسريب','عكارة','تنظيف'],
        'التصميمات الهندسية' => ['تصميم','3d','2d','مخطط','رسومات','design'],
    ];
    foreach ($serviceKw as $svc => $kws) {
        foreach ($kws as $kw) {
            if ($kw !== '' && mb_strpos($lower, mb_strtolower($kw,'UTF-8')) !== false) {
                if (!isset($customer['services']) || !is_array($customer['services'])) $customer['services'] = [];
                if (!in_array($svc, $customer['services'], true)) {
                    $customer['services'][] = $svc;
                }
                break;
            }
        }
    }


    // Detect business type
    if (empty($customer['business_type'])) {
        $businessPatterns = [
            '/مطعم|كافيه|طعام|restaurant|cafe|food/' => 'مطاعم وكافيهات',
            '/عيادة|طبيب|صيدلية|medical|clinic|pharmacy/' => 'طبي وصحي',
            '/محاماة|محامي|قانوني|law|legal|lawyer/' => 'خدمات قانونية',
            '/متجر|تجارة|بيع|شراء|store|shop|retail/' => 'تجارة إلكترونية',
            '/تعليم|مدرسة|دورات|تدريب|education|training/' => 'تعليمي',
            '/عقارات|شقق|فيلا|أراضي|real estate|property/' => 'عقاري',
            '/تصميم|جرافيك|إعلان|design|advertising/' => 'تصميم وإعلان',
            '/استشارات|خدمات|consulting|services/' => 'استشارات',
            '/تكنولوجيا|برمجة|تطبيقات|tech|programming/' => 'تكنولوجيا',
            '/مصنع|تصنيع|manufacturing|factory/' => 'تصنيع وإنتاج'
        ];

        foreach ($businessPatterns as $regex => $type) {
            if (preg_match($regex . 'u', $lower ?? '')) {
                $customer['business_type'] = $type;
                break;
            }
        }
    }

    // Update qualification status
    if (preg_match('/زائر|visitor|visit/iu', $input ?? '')) {
        $customer['qualification_status'] = 'visitor';
    } elseif (preg_match('/عارض|exhibitor|exhibit|مشارك/iu', $input ?? '')) {
        $customer['qualification_status'] = 'exhibitor';
        $customer['lead_score'] += 10;
    }

    // Detect interests
    $interestPatterns = [
        '/معرض|exhibition|expo|fair/' => 'معارض دولية',
        '/تصدير|export|international/' => 'التصدير والاستيراد',
        '/منتج|product|manufacturing/' => 'عرض المنتجات',
        '/شركة|company|business/' => 'تطوير الأعمال',
        '/تسويق|marketing|promotion/' => 'التسويق الدولي'
    ];

    foreach ($interestPatterns as $regex => $interest) {
        if (preg_match($regex . 'u', $lower ?? '') && !in_array($interest, $customer['interests'])) {
            $customer['interests'][] = $interest;
            $customer['lead_score'] += 2;
        }
    }

    if (count($customer['interests']) > 10) {
        $customer['interests'] = array_slice($customer['interests'], -10);
    }
}

function trackConversionSignals(string $phone, string $userInput, string $aiResponse): void {
    $customer = loadCustomerData($phone);
    $input = mb_strtolower($userInput ?? '', 'UTF-8');
    $signals = [];
    $newlyQualified = false;

    if (preg_match('/مهتم|interested|أريد|want|محتاج|need/u', $input)) {
        $signals['interest_expressed'] = time();
        $customer['lead_score'] += 5;
    }

    if (preg_match('/سعر|price|cost|تكلفة|كام|كم/u', $input)) {
        $signals['price_inquiry'] = time();
        $customer['lead_score'] += 8;
    }

    if (preg_match('/متى|when|موعد|date|تاريخ/u', $input)) {
        $signals['timeline_inquiry'] = time();
        $customer['lead_score'] += 6;
    }

    if (preg_match('/تسجيل|register|اشتراك|booking/u', $input)) {
        $signals['registration_intent'] = time();
        $customer['lead_score'] += 15;
        if ($customer['qualification_status'] !== 'qualified') {
            $customer['qualification_status'] = 'qualified';
            $newlyQualified = true;
        }
    }

    if (preg_match('/اتصال|call|تواصل|contact|رقم|number/u', $input)) {
        $signals['contact_request'] = time();
        $customer['lead_score'] += 12;
        logMessage('Contact requested', 'CONVERSION', $phone);
        $newlyQualified = true; // إشارة قوية تستدعي الإشعار
    }

    if (!empty($signals)) {
        $customer['conversion_signals'] = array_merge($customer['conversion_signals'] ?? [], $signals);
        saveCustomerData($phone, $customer);

        // إشعار فريق المبيعات إذا كانت هناك إشارة مهمة
        if ($newlyQualified) {
            $signalType = isset($signals['registration_intent']) ? 'نية تسجيل' : 'طلب اتصال';
            notifySalesTeam($phone, $customer, $signalType);
        }
    }
}

// ======== Conversation Management ========
function loadConversation(string $phone): array {
    $file = "chats/" . basename($phone) . ".json";
    return file_exists($file) ?
        (json_decode(file_get_contents($file), true) ?? []) :
        [];
}

function saveConversation(string $phone, array $conversation): void {
    $file = "chats/" . basename($phone) . ".json";
    @file_put_contents($file, json_encode($conversation, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
}

function addToChatLog(string $phone, string $content, string $role, array $meta = []): void {
    $conversation = loadConversation($phone);
    $now = time();
    $conversation[] = [
        'role' => $role,
        'content' => $content,
        'timestamp' => $now,
        'ts' => $now, // keep CRM compatibility
        'meta' => $meta // بيانات إضافية مثل نوع الرسالة أو ID الميديا أو الكود
    ];
    saveConversation($phone, $conversation);
}

function sanitizePathSegment(string $value): string {
    $sanitized = preg_replace('/[^A-Za-z0-9_\-]/', '', $value);
    return $sanitized !== '' ? $sanitized : 'customer';
}

function fileExtensionFromMime(string $mime, string $fallback = 'bin'): string {
    $mime = strtolower(trim($mime));
    if ($mime === '') return $fallback;
    $map = [
        'image/jpeg' => 'jpg',
        'image/jpg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
        'image/gif' => 'gif',
        'video/mp4' => 'mp4',
        'video/quicktime' => 'mov',
        'video/webm' => 'webm',
        'application/pdf' => 'pdf',
    ];
    if (isset($map[$mime])) return $map[$mime];
    if (strpos($mime, '/') !== false) {
        $candidate = preg_replace('/[^a-z0-9]/', '', explode('/', $mime)[1] ?? '');
        if ($candidate !== '') return $candidate;
    }
    return $fallback;
}

function buildPublicUrlFromRelativePath(string $relativePath): string {
    $relativePath = ltrim(str_replace('\\', '/', $relativePath), '/');
    $base = resolvePublicBaseUrl();
    if ($base === '') {
        return '/' . $relativePath;
    }
    return rtrim($base, '/') . '/' . $relativePath;
}

function resolvePublicBaseUrl(): string {
    global $config;
    $base = trim((string)($config['public_base_url'] ?? ''));
    if ($base !== '' && stripos($base, 'YOURDOMAIN.com') === false) {
        return $base;
    }

    $host = trim((string)($_SERVER['HTTP_HOST'] ?? ''));
    if ($host === '') {
        return '';
    }
    $https = strtolower((string)($_SERVER['HTTPS'] ?? ''));
    $scheme = ($https === 'on' || $https === '1') ? 'https' : 'http';
    return $scheme . '://' . $host;
}

function copyMediaToPublicCustomerDir(string $customerId, string $srcPath, string $mime = ''): ?array {
    if (!is_file($srcPath)) return null;

    $safeCustomerId = sanitizePathSegment($customerId);
    $targetDir = "customers_media/{$safeCustomerId}";
    ensureDir($targetDir);

    $extFromSource = strtolower((string)pathinfo($srcPath, PATHINFO_EXTENSION));
    if ($extFromSource === '') {
        $extFromSource = fileExtensionFromMime($mime !== '' ? $mime : guessMimeFromPath($srcPath), 'bin');
    }

    $rand = substr(sha1($srcPath . '|' . microtime(true) . '|' . mt_rand()), 0, 10);
    $fileName = 'preview_' . date('Ymd_His') . '_' . $rand . '.' . $extFromSource;
    $destPath = $targetDir . '/' . $fileName;

    if (!@copy($srcPath, $destPath)) {
        return null;
    }

    $relative = ltrim(str_replace('\\', '/', $destPath), '/');
    return [
        'path' => $destPath,
        'relative_path' => $relative,
        'url' => buildPublicUrlFromRelativePath($relative),
    ];
}

function registerIncomingPreviewMedia(string $customerId, string $platform, string $sourceSenderId, array &$customer, array &$mediaFiles): array {
    $added = [];
    $existing = [];
    if (!empty($customer['previews']) && is_array($customer['previews'])) {
        foreach ($customer['previews'] as $p) {
            $k = md5((string)($p['url'] ?? '') . '|' . (string)($p['type'] ?? '') . '|' . (string)($p['note'] ?? ''));
            $existing[$k] = true;
        }
    } else {
        $customer['previews'] = [];
    }

    foreach ($mediaFiles as &$mf) {
        $type = trim((string)($mf['type'] ?? ''));
        $path = trim((string)($mf['path'] ?? ''));
        $mime = trim((string)($mf['mime'] ?? ''));
        $note = trim((string)($mf['note'] ?? ''));
        $externalUrl = trim((string)($mf['url'] ?? ''));

        if ($note === 'extracted_video_frame') {
            continue; // avoid duplicate preview entries
        }
        if (!in_array($type, ['image', 'video', 'document'], true)) {
            continue;
        }

        $publicUrl = '';
        $publicPath = '';
        if ($path !== '' && is_file($path)) {
            $copied = copyMediaToPublicCustomerDir($customerId, $path, $mime);
            if ($copied) {
                $publicUrl = (string)($copied['url'] ?? '');
                $publicPath = (string)($copied['path'] ?? '');
                $mf['public_url'] = $publicUrl;
                $mf['public_path'] = $publicPath;
                $mf['public_relative_path'] = (string)($copied['relative_path'] ?? '');
            }
        }

        if ($publicUrl === '' && $externalUrl !== '') {
            $publicUrl = $externalUrl;
            $mf['public_url'] = $publicUrl;
        }

        if ($publicUrl === '') {
            continue;
        }

        $preview = [
            'url' => $publicUrl,
            'ts' => time(),
            'platform' => $platform,
            'type' => $type,
            'note' => $note,
            'local_path' => $publicPath,
            'source_sender_id' => $sourceSenderId,
        ];
        $key = md5($preview['url'] . '|' . $preview['type'] . '|' . $preview['note']);
        if (isset($existing[$key])) {
            continue;
        }
        $existing[$key] = true;

        $customer['previews'][] = $preview;
        $added[] = $preview;
    }
    unset($mf);

    if (count($customer['previews']) > 200) {
        $customer['previews'] = array_slice($customer['previews'], -200);
    }

    return $added;
}

function buildPreviewTimelineEventText(array $addedPreviews): string {
    $lines = ["📎 تم حفظ معاينة جديدة داخل CRM"];
    foreach (array_slice($addedPreviews, 0, 3) as $p) {
        $lines[] = "🖼️ " . (string)($p['url'] ?? '');
    }
    $remaining = count($addedPreviews) - 3;
    if ($remaining > 0) {
        $lines[] = "… +{$remaining} ملف إضافي";
    }
    return implode("\n", $lines);
}


// ======== AI Response Generation ========

/**
 * ===========================
 * Website grounding utilities
 * ===========================
 * The goal: keep answers strictly grounded in paradisegroup-eg.com content.
 */

function ensureDir(string $dir): void {
    if (!is_dir($dir)) {
        @mkdir($dir, 0777, true);
    }
}

function fetchUrl(string $url, int $timeout = 12): ?string {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_TIMEOUT => $timeout,
        CURLOPT_CONNECTTIMEOUT => 6,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_HTTPHEADER => [
            'User-Agent: ParadiseBot/1.0 (+https://paradisegroup-eg.com)'
        ],
    ]);
    $res = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($res === false || $code < 200 || $code >= 300) return null;
    return $res;
}

function htmlToPlainText(string $html): string {
    // Drop scripts/styles
    $html = preg_replace('/<script\b[^>]*>.*?<\/script>/is', ' ', $html);
    $html = preg_replace('/<style\b[^>]*>.*?<\/style>/is', ' ', $html);
    // Replace common block tags with new lines
    $html = preg_replace('/<\/(p|div|h1|h2|h3|h4|h5|h6|li|ul|ol|section|br)\s*>/i', "\n", $html);
    $text = strip_tags($html);
    $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    // Normalize whitespace
    $text = preg_replace("/[ \t]+/", " ", $text);
    $text = preg_replace("/\n{3,}/", "\n\n", $text);
    $text = trim($text);
    return $text;
}

function fetchWebsiteTextCached(string $url, int $ttlSeconds = 86400): ?string {
    ensureDir('cache');
    $key = md5($url);
    $cacheFile = "cache/site_{$key}.txt";

    if (is_file($cacheFile) && (time() - filemtime($cacheFile) < $ttlSeconds)) {
        $cached = @file_get_contents($cacheFile);
        if (is_string($cached) && $cached !== '') return $cached;
    }

    $html = fetchUrl($url, 14);
    if (!$html) return null;

    $text = htmlToPlainText($html);
    if ($text === '') return null;

    @file_put_contents($cacheFile, $text);
    return $text;
}

function getLastUserMessageText(array $conversation): string {
    for ($i = count($conversation) - 1; $i >= 0; $i--) {
        if (($conversation[$i]['role'] ?? '') === 'user') {
            return (string)($conversation[$i]['content'] ?? '');
        }
    }
    return '';
}

function parseSitemapLocs(string $xml): array {
    $xml = trim($xml);
    if ($xml === '') return [];
    if (!function_exists('simplexml_load_string')) return [];

    $prev = libxml_use_internal_errors(true);
    $doc = simplexml_load_string($xml);
    libxml_clear_errors();
    libxml_use_internal_errors($prev);

    if (!$doc) return [];

    $locs = [];
    $nodes = $doc->xpath('//*[local-name()="loc"]');
    if (is_array($nodes)) {
        foreach ($nodes as $node) {
            $u = trim((string)$node);
            if ($u !== '') $locs[] = $u;
        }
    }

    return array_values(array_unique($locs));
}

function fetchWebsiteSitemapUrlsCached(string $websiteBase, int $ttlSeconds = 43200): array {
    ensureDir('cache');
    $cacheFile = "cache/sitemap_" . md5($websiteBase) . ".json";

    if (is_file($cacheFile) && (time() - filemtime($cacheFile) < $ttlSeconds)) {
        $cached = json_decode((string)@file_get_contents($cacheFile), true);
        if (is_array($cached)) return $cached;
    }

    $root = rtrim($websiteBase, '/') . '/sitemap.xml';
    $rootXml = fetchUrl($root, 14);
    if (!$rootXml) {
        @file_put_contents($cacheFile, json_encode([], JSON_UNESCAPED_UNICODE));
        return [];
    }

    $rootLocs = parseSitemapLocs($rootXml);
    $sitemapXmlUrls = [];
    $pageUrls = [];
    foreach ($rootLocs as $loc) {
        if (preg_match('/\.xml(\?|$)/i', $loc)) {
            $sitemapXmlUrls[] = $loc;
        } else {
            $pageUrls[] = $loc;
        }
    }

    // Handle sitemap index (one level)
    foreach (array_slice($sitemapXmlUrls, 0, 8) as $smUrl) {
        $smXml = fetchUrl($smUrl, 14);
        if (!$smXml) continue;
        foreach (parseSitemapLocs($smXml) as $loc) {
            if (!preg_match('/\.xml(\?|$)/i', $loc)) {
                $pageUrls[] = $loc;
            }
        }
    }

    $baseHost = parse_url($websiteBase, PHP_URL_HOST);
    $filtered = [];
    foreach ($pageUrls as $u) {
        $host = parse_url($u, PHP_URL_HOST);
        if ($host && $baseHost && strcasecmp($host, $baseHost) !== 0) continue;
        if (preg_match('/\.(jpg|jpeg|png|gif|webp|pdf|zip|mp4|webm)(\?|$)/i', $u)) continue;
        $filtered[] = $u;
    }
    $filtered = array_values(array_unique($filtered));

    @file_put_contents($cacheFile, json_encode($filtered, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
    return $filtered;
}

function buildWebsiteGroundingPrompt(string $userText): string {
    global $config;

    $websiteBase = rtrim($config['company_website'] ?? 'https://paradisegroup-eg.com', '/');
    $u = mb_strtolower($userText, 'UTF-8');

    // Tokens for better excerpts (avoid long irrelevant headers)
    $cleanForTokens = preg_replace('/[^\p{L}\p{N}\s]+/u', ' ', $userText);
    $rawTokens = preg_split('/\s+/', trim($cleanForTokens));
    $tokens = [];
    foreach ($rawTokens as $w) {
        $w = trim($w);
        if ($w === '') continue;
        if (mb_strlen($w, 'UTF-8') < 4) continue;
        $lw = mb_strtolower($w, 'UTF-8');
        if (in_array($lw, ['عايز','محتاج','ممكن','لو','هل','في','من','على','عن','ايه','اي','عاوز'], true)) continue;
        $tokens[] = $lw;
        if (count($tokens) >= 6) break;
    }

    $configuredUrls = $config['website_grounding_urls'] ?? [];
    if (!is_array($configuredUrls) || empty($configuredUrls)) {
        $configuredUrls = [
            $websiteBase . '/services',
            $websiteBase . '/contact',
            $websiteBase . '/pages/about',
        ];
    }

    // Add relevant service page(s) based on keywords
    $services = [
        'pools' => [
            'url' => $websiteBase . '/services/hmamat-alsbah',
            'kw'  => ['مسبح', 'حمام', 'حمامات', 'pool', 'swimming', 'اسكيمر', 'سكيمر', 'overflow', 'infinity', 'هورايزون', 'اوفر', 'أوفر'],
        ],
        'water_features' => [
            'url' => $websiteBase . '/services/alshlalat-oalnaforat',
            'kw'  => ['شلال', 'شلالات', 'نافورة', 'نوافير', 'fountain', 'waterfall', 'water feature', 'rgb', 'ضباب', 'بخار'],
        ],
        'landscape' => [
            'url' => $websiteBase . '/services/allandskyb',
            'kw'  => ['لاندسكيب', 'landscape', 'garden', 'حديقة', 'تنسيق', 'زراعة', 'ري', 'نجيل', 'برجولا', 'جلسات', 'ممرات'],
        ],
        'finishing' => [
            'url' => $websiteBase . '/services/altshtybat',
            'kw'  => ['تشطيب', 'تشطيبات', 'واجهات', 'كسوة', 'حجر', 'رخام', 'دهانات', 'تجديد', 'إحلال', 'احلال', 'renovation', 'finishing'],
        ],
        'maintenance' => [
            'url' => $websiteBase . '/services/alsyan',
            'kw'  => ['صيانة', 'maintenance', 'فلتر', 'فلاتر', 'مضخة', 'كلور', 'تعقيم', 'تسريب', 'عكارة', 'ضعف', 'تنظيف'],
        ],
        'design' => [
            'url' => $websiteBase . '/services/altsmymat-alhndsy',
            'kw'  => ['تصميم', 'design', 'هندسي', 'هندسية', 'رسومات', '2d', '3d', 'موديل', 'مخطط', 'بلان'],
        ],
        'water_treatment' => [
            'url' => $websiteBase . '/services/water-treatment-solutions',
            'kw'  => ['معالجة', 'تنقية', 'تحاليل', 'تحليل', 'water treatment', 'مياه'],
        ],
        'infrastructure' => [
            'url' => $websiteBase . '/services/infrastructure-protection',
            'kw'  => ['حماية', 'بنية تحتية', 'infrastructure', 'protection', 'زلازل', 'اهتزاز', 'طوارئ', 'إضاءة طوارئ'],
        ],
        'portfolio' => [
            'url' => $websiteBase . '/projects',
            'kw'  => ['portfolio', 'أعمال', 'اعمال', 'مشاريع', 'projects', 'سابقة'],
        ],
    ];

    $serviceMatchedUrls = [];
    foreach ($services as $s) {
        foreach ($s['kw'] as $kw) {
            if ($kw !== '' && mb_strpos($u, mb_strtolower($kw, 'UTF-8')) !== false) {
                $serviceMatchedUrls[] = $s['url'];
                break;
            }
        }
    }

    $urls = array_values(array_unique(array_merge($configuredUrls, $serviceMatchedUrls)));

    if ($config['website_use_sitemap'] ?? true) {
        $sitemapUrls = fetchWebsiteSitemapUrlsCached($websiteBase, (int)($config['website_sitemap_ttl'] ?? 43200));
        $candidateLimit = max(4, (int)($config['website_sitemap_candidate_limit'] ?? 12));

        $scoredSitemap = [];
        foreach ($sitemapUrls as $sUrl) {
            if (in_array($sUrl, $urls, true)) continue;
            $lowerUrl = mb_strtolower($sUrl, 'UTF-8');
            $score = 0;
            foreach ($tokens as $tkn) {
                if ($tkn !== '' && mb_strpos($lowerUrl, $tkn) !== false) {
                    $score += 2;
                }
            }
            if (strpos($lowerUrl, '/services') !== false) $score += 1;
            $scoredSitemap[] = ['url' => $sUrl, 'score' => $score];
        }

        usort($scoredSitemap, function ($a, $b) {
            return ($b['score'] <=> $a['score']);
        });

        foreach (array_slice($scoredSitemap, 0, $candidateLimit) as $row) {
            $urls[] = $row['url'];
        }
    }

    $urls = array_values(array_unique($urls));

    // Build compact excerpts
    $maxTotal = (int)($config['website_facts_max_chars'] ?? 6000);
    $perUrlMax = (int)($config['website_facts_per_url_chars'] ?? 1600);
    $maxSources = max(1, (int)($config['website_facts_max_sources'] ?? 6));
    $minSources = max(1, min($maxSources, (int)($config['website_facts_min_sources'] ?? 3)));

    $candidates = [];
    foreach ($urls as $url) {
        $txt = fetchWebsiteTextCached($url, (int)($config['website_facts_ttl'] ?? 21600));
        if (!$txt) continue;

        // Remove any pricing hints from website text (bot must never discuss prices)
        $txt = preg_replace('/^.*(سعر|أسعار|تكلفة|جنيه|ج\.?م|EGP|USD|\$|€|£).*$/imu', '', $txt);
        $txtLower = mb_strtolower($txt, 'UTF-8');

        // Try to build a relevant excerpt around user keywords (if found)
        $excerpt = '';
        $tokenHits = 0;
        foreach ($tokens as $tkn) {
            $pos = mb_strpos($txtLower, $tkn);
            if ($pos !== false) {
                $tokenHits++;
                if ($excerpt === '') {
                    $start = max(0, $pos - (int)($perUrlMax / 3));
                    $excerpt = mb_substr($txt, $start, $perUrlMax, 'UTF-8');
                }
            }
        }
        if ($excerpt === '') {
            $excerpt = mb_substr($txt, 0, $perUrlMax, 'UTF-8');
        }

        $score = $tokenHits;
        if (in_array($url, $serviceMatchedUrls, true)) $score += 3;
        if (in_array($url, $configuredUrls, true)) $score += 2;
        if (empty($tokens)) $score += 1;

        $candidates[] = [
            'url' => $url,
            'excerpt' => $excerpt,
            'score' => $score,
        ];
    }

    if (empty($candidates)) return '';

    usort($candidates, function ($a, $b) {
        if ($a['score'] === $b['score']) return 0;
        return ($b['score'] <=> $a['score']);
    });

    $selected = [];
    foreach ($candidates as $c) {
        if (count($selected) >= $maxSources) break;
        if ($c['score'] > 0 || count($selected) < $minSources) {
            $selected[] = $c;
        }
    }
    if (empty($selected)) return '';

    $parts = [];
    $total = 0;
    foreach ($selected as $c) {
        $block = "SOURCE: {$c['url']}\n" . $c['excerpt'];
        $blockLen = mb_strlen($block, 'UTF-8');
        if ($total + $blockLen > $maxTotal) break;
        $parts[] = $block;
        $total += $blockLen + 2;
    }

    if (empty($parts)) return '';

    return "WEBSITE FACTS (استخدمها فقط بدون أي افتراضات):\n"
        . implode("\n\n---\n\n", $parts)
        . "\n\nقواعد إلزامية:\n"
        . "- لا تقدّم أسعار/نطاقات/تقديرات/أرقام مالية نهائيًا.\n"
        . "- لو السؤال مش متغطّي بوضوح في WEBSITE FACTS: قل مش متأكد 100% ووجّه العميل للواتساب/نموذج التواصل.\n";
}

/**
 * Hard guardrail: NO prices in responses.
 */
function responseLooksLikePrice(string $text): bool {
    $t = $text;

    // Currency symbols / ISO codes
    if (preg_match('/[$€£]\s*\d|(\d[\d\s,.]{0,12}\s*[$€£])/u', $t)) return true;
    if (preg_match('/\b(USD|EUR|SAR|AED|KWD|QAR|EGP)\b/iu', $t) && preg_match('/\d/', $t)) return true;

    // Currency words with numbers (Arabic/English)
    if (preg_match('/\d[\d\s,.]{0,12}\s*(جنيه|ج\.م|EGP|L\.E|LE|£|ريال|درهم|دينار|دولار)/iu', $t)) return true;

    // Large numeric ranges that look like money: "من 5000 إلى 12000"
    if (preg_match('/\bمن\s*\d{4,}[\d\s,.]*\s*(إلى|لحد|حتى)\s*\d{4,}/iu', $t)) return true;

    // "starts from" style with large numbers
    if (preg_match('/\b(تبدأ|بيبدأ|starting)\s*(من)?\s*\d{4,}/iu', $t)) return true;

    // Money magnitude words near digits (10k / 100 ألف)
    if (preg_match('/\d[\d\s,.]{0,12}\s*(ألف|آلاف|مليون|ملايين|k|K)\b/u', $t)) return true;

    // "سعر/تكلفة" + numbers
    if (preg_match('/\b(سعر|تكلفة|cost|price)\b.*\d/iu', $t)) return true;

    return false;
}


function enforceNoPricing(string $aiResponse, string $userText = ''): string {
    global $config;

    if (!responseLooksLikePrice($aiResponse)) return $aiResponse;

    $handoverWhatsapp = $config['handover_whatsapp'] ?? '+20 10 19109992';
    $handoverCall = $config['handover_call'] ?? $handoverWhatsapp;
    $websiteBase = rtrim($config['company_website'] ?? 'https://paradisegroup-eg.com', '/');
    $contactFormUrl = $config['contact_form_url'] ?? ($websiteBase . '/contact');

    $isEnglish = preg_match('/[A-Za-z]/', $userText) && !preg_match('/[\x{0600}-\x{06FF}]/u', $userText);

    if ($isEnglish) {
        return "To prepare an accurate quotation, we need the location, dimensions/area, and photos/videos of the site. We don't share pricing in chat.\n\nPlease send:\n- City/area in Egypt\n- Place type (villa/hotel/club/etc.)\n- Approx. size/dimensions + photos/videos\n\nWhatsApp: {$handoverWhatsapp}\nCall: {$handoverCall}\nForm: {$contactFormUrl}";
    }

    return "علشان نجهز **عرض سعر/معاينة** بدقة، محتاجين موقع المشروع والمقاسات/المساحة وصور أو فيديو للمكان.\nومش بنعرض أسعار داخل الشات.\n\nابعت لنا:\n- المدينة/المنطقة\n- نوع المكان (فيلا/فندق/نادي...)\n- المساحة أو المقاسات + صور/فيديو\n\nواتساب: {$handoverWhatsapp}\nاتصال: {$handoverCall}\nنموذج: {$contactFormUrl}";
}



/**
 * Remove any leftover "[BUTTONS: ...]" markup (we do NOT use buttons).
 */
function stripButtonsMarkup(string $text): string {
    // Remove bracketed buttons pattern
    $text = preg_replace('/\[\s*buttons\s*:\s*.*?\]/isu', '', $text);
    $text = preg_replace('/\[\s*BUTTONS\s*:\s*.*?\]/isu', '', $text);
    return trim($text);
}

/**
 * Show preview number only after user intent is clear.
 */
function ensurePreviewInstruction(string $text, array &$customer, string $intent): string {
    global $config;

    if (!($config['ensure_preview_line'] ?? true)) return trim($text);

    $preview = trim((string)($config['preview_number_display'] ?? '+20 10 94945225'));
    if ($preview === '') return trim($text);

    if (!isActionableIntent($intent)) {
        return stripPreviewInstructionLine($text, $preview);
    }

    $needle1 = preg_replace('/\s+/', '', $preview); // normalize spaces
    $hay = preg_replace('/\s+/', '', (string)$text);

    // Already has the instruction.
    if (strpos($hay, $needle1) !== false || mb_strpos($text, 'لإرسال المعاينة') !== false) {
        $customer['preview_instruction_last_sent'] = time();
        $customer['preview_instruction_last_intent'] = $intent;
        return trim($text);
    }

    // Avoid repeating it too frequently for the same intent.
    $cooldown = (int)($config['preview_instruction_cooldown_seconds'] ?? 21600); // 6h
    if ($cooldown < 0) $cooldown = 0;
    $lastSent = (int)($customer['preview_instruction_last_sent'] ?? 0);
    $lastIntent = (string)($customer['preview_instruction_last_intent'] ?? '');
    if ($lastSent > 0 && (time() - $lastSent) < $cooldown && $lastIntent === $intent) {
        return trim($text);
    }

    $line = "📸 لإرسال المعاينة على الرقم التالي: {$preview}";
    $text = rtrim($text);
    if ($text === '') {
        $customer['preview_instruction_last_sent'] = time();
        $customer['preview_instruction_last_intent'] = $intent;
        return $line;
    }

    // Keep it clean: one blank line max
    $customer['preview_instruction_last_sent'] = time();
    $customer['preview_instruction_last_intent'] = $intent;
    return $text . "\n\n" . $line;
}

function ensureFirstReplyGreeting(string $text, bool $isNewConversation, array $customer, string $intent, string $userInput = ''): string {
    if (!$isNewConversation) return trim($text);

    $result = trim($text);
    $isEnglish = isEnglishOnlyInput($userInput !== '' ? $userInput : $result);
    $name = trim((string)($customer['name'] ?? ''));

    $hasGreeting = (bool)preg_match('/(اهلا|أهلا|مرحبا|مرحب|welcome|hello|hi)/iu', $result);
    if (!$hasGreeting) {
        $greeting = $isEnglish
            ? "Welcome to Paradise Group."
            : ($name !== '' ? "أهلاً {$name}، نورتنا في Paradise Group." : "أهلاً بيك في Paradise Group.");
        $result = $greeting . ($result !== '' ? "\n\n" . $result : '');
    }

    // Make sure first reply asks for the issue/request if no question exists yet.
    $hasQuestion = (mb_strpos($result, '؟') !== false || strpos($result, '?') !== false);
    if (!$hasQuestion) {
        $ask = $isEnglish
            ? "Can you briefly explain your issue or request so I can help accurately?"
            : "ممكن توضح مشكلتك أو طلبك باختصار علشان أقدر أساعدك بدقة؟";
        $result = rtrim($result) . ($result !== '' ? "\n\n" : '') . $ask;
    }

    return trim($result);
}

function stripPreviewInstructionLine(string $text, string $previewNumber = ''): string {
    $lines = preg_split('/\R/u', (string)$text) ?: [];
    $filtered = [];
    $previewCompact = preg_replace('/\s+/', '', (string)$previewNumber);

    foreach ($lines as $line) {
        $raw = (string)$line;
        $trim = trim($raw);
        $compact = preg_replace('/\s+/', '', $trim);

        if ($trim !== '' && mb_strpos($trim, 'لإرسال المعاينة') !== false) {
            continue;
        }
        if (
            $trim !== '' &&
            $previewCompact !== '' &&
            strpos($compact, $previewCompact) !== false &&
            (mb_strpos($trim, 'معاينة') !== false || mb_strpos($trim, 'معاينه') !== false || mb_stripos($trim, 'preview') !== false)
        ) {
            continue;
        }

        $filtered[] = $raw;
    }

    $clean = trim(implode("\n", $filtered));
    $clean = preg_replace("/\n{3,}/", "\n\n", $clean);
    return trim((string)$clean);
}

function guessMimeFromPath(string $path): string {
    if (function_exists('finfo_open')) {
        $fi = @finfo_open(FILEINFO_MIME_TYPE);
        if ($fi) {
            $mime = @finfo_file($fi, $path);
            @finfo_close($fi);
            if (is_string($mime) && $mime !== '') return $mime;
        }
    }
    // Fallback by extension
    $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
    return match($ext) {
        'jpg', 'jpeg' => 'image/jpeg',
        'png' => 'image/png',
        'webp' => 'image/webp',
        'gif' => 'image/gif',
        'mp4' => 'video/mp4',
        'mp3' => 'audio/mpeg',
        'wav' => 'audio/wav',
        default => 'application/octet-stream',
    };
}

/**
 * Convert local file to data URL for vision input (size limited).
 */
function localFileToDataUrl(string $path, ?string $mime = null, int $maxBytes = 3500000): ?string {
    if (!$path || !is_file($path)) return null;
    $size = filesize($path);
    if ($size === false || $size <= 0) return null;
    if ($size > $maxBytes) return null;

    $mime = $mime ?: guessMimeFromPath($path);
    $data = @file_get_contents($path);
    if ($data === false) return null;
    $b64 = base64_encode($data);
    return "data:{$mime};base64,{$b64}";
}

/**
 * Prepare OpenAI messages: sanitize extra keys + attach images (vision) when present.
 */
function prepareMessagesForOpenAI(array $conversation): array {
    global $config;

    $maxVisionBytes = (int)($config['vision_max_bytes'] ?? 3500000);

    $messages = [];
    foreach ($conversation as $m) {
        $role = $m['role'] ?? '';
        if (!in_array($role, ['system','user','assistant'], true)) continue;

        $content = $m['content'] ?? '';
        $meta = $m['meta'] ?? [];

        if ($role !== 'user') {
            $messages[] = [
                'role' => $role,
                'content' => (string)$content
            ];
            continue;
        }

        $text = (string)$content;
        $parts = [];

        if (trim($text) !== '') {
            $parts[] = ['type' => 'text', 'text' => $text];
        }

        $mediaFiles = [];
        if (is_array($meta) && !empty($meta['media_files']) && is_array($meta['media_files'])) {
            $mediaFiles = $meta['media_files'];
        }

        // Attach only images (including extracted video frames)
        $attachedAnyImage = false;
        foreach ($mediaFiles as $f) {
            $ft = $f['type'] ?? '';
            if ($ft !== 'image') continue;

            $path = $f['path'] ?? '';
            $mime = $f['mime'] ?? null;
            $dataUrl = localFileToDataUrl($path, $mime, $maxVisionBytes);
            if ($dataUrl) {
                $parts[] = [
                    'type' => 'image_url',
                    'image_url' => ['url' => $dataUrl]
                ];
                $attachedAnyImage = true;
            }
        }

        // If no images attached (or message is plain text), send as normal string
        if (!$attachedAnyImage && count($parts) === 1 && ($parts[0]['type'] ?? '') === 'text') {
            $messages[] = ['role' => 'user', 'content' => $text];
        } else {
            if (empty($parts)) $parts[] = ['type' => 'text', 'text' => '[رسالة بدون نص]'];
            $messages[] = ['role' => 'user', 'content' => $parts];
        }
    }

    return $messages;
}

/**
 * Try extracting a single frame from a video file (requires ffmpeg).
 * Returns jpg path or null.
 */
function extractVideoFrame(string $videoPath): ?string {
    if (!$videoPath || !is_file($videoPath)) return null;

    $ffmpeg = trim((string)@shell_exec('command -v ffmpeg 2>/dev/null'));
    if ($ffmpeg === '') return null;

    ensureDir('imports/frames');
    $hash = substr(sha1($videoPath . '|' . filesize($videoPath)), 0, 12);
    $out = "imports/frames/frame_{$hash}.jpg";

    // Extract near 1s (works for most videos)
    $cmd = escapeshellcmd($ffmpeg) . ' -y -ss 00:00:01 -i ' . escapeshellarg($videoPath) . ' -vframes 1 -q:v 3 ' . escapeshellarg($out) . ' 2>/dev/null';
    @shell_exec($cmd);

    return is_file($out) ? $out : null;
}

/**
 * Download external media (Messenger/Instagram attachment URL).
 */
function downloadExternalMedia(string $url, string $kind = 'file'): ?array {
    if (!$url) return null;

    ensureDir('imports/ext');

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_TIMEOUT => 25,
        CURLOPT_CONNECTTIMEOUT => 8,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_USERAGENT => 'ParadiseBot/1.0'
    ]);

    $data = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $ctype = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);

    if ($data === false || $code < 200 || $code >= 300) return null;

    $mime = is_string($ctype) ? trim(explode(';', $ctype)[0]) : 'application/octet-stream';
    $ext = 'bin';
    if ($mime && strpos($mime, '/') !== false) {
        $ext = preg_replace('/[^a-z0-9]/i', '', explode('/', $mime)[1]) ?: 'bin';
    } elseif ($kind === 'image') $ext = 'jpg';
    elseif ($kind === 'video') $ext = 'mp4';
    elseif ($kind === 'audio') $ext = 'mp3';

    $hash = substr(sha1($url . '|' . strlen($data)), 0, 16);
    $dest = "imports/ext/{$kind}_{$hash}.{$ext}";
    @file_put_contents($dest, $data);

    if (!is_file($dest)) return null;
    return ['path' => $dest, 'mime' => $mime];
}

function getAIResponse(array $conversation): ?string {
    global $config;

    // 1) قلّل التاريخ المستخدم فعليًا لتقليل التكلفة
    $maxHist = (int)($config['ai_max_history'] ?? 16);
    if ($maxHist > 0 && count($conversation) > $maxHist) {
        // احتفظ بأول رسالة system ثم آخر ($maxHist-1)
        $system = $conversation[0]['role'] === 'system' ? array_shift($conversation) : null;
        $conversation = array_slice($conversation, -($maxHist - 1));
        if ($system) array_unshift($conversation, $system);
    }

        // 1.5) Website grounding (official site excerpts)
    $lastUserText = getLastUserMessageText($conversation);
    $grounding = $lastUserText !== '' ? buildWebsiteGroundingPrompt($lastUserText) : '';
    if ($grounding !== '') {
        // Insert after the base system prompt (keep priority)
        $insertAt = (!empty($conversation) && ($conversation[0]['role'] ?? '') === 'system') ? 1 : 0;
        array_splice($conversation, $insertAt, 0, [['role' => 'system', 'content' => $grounding]]);
    }

// Prepare sanitized messages for OpenAI (and cache)
    $messagesForAI = prepareMessagesForOpenAI($conversation);

    // 2) كاش مبني على محتوى الرسائل
    $cacheKey = md5(json_encode($messagesForAI, JSON_UNESCAPED_UNICODE));
    $cacheFile = "cache/ai_{$cacheKey}.json";
    $ttl = (int)($config['ai_cache_ttl'] ?? 1800);
    if (is_file($cacheFile) && (time() - filemtime($cacheFile) < $ttl)) {
        $cached = json_decode(@file_get_contents($cacheFile), true);
        if (!empty($cached['content'])) {
            return (string)$cached['content'];
        }
    }

    // 3) إعدادات موديل أخف
    $model = $config['ai_model'] ?? 'gpt-4o-mini';
    $temperature = (float)($config['ai_temperature'] ?? 0.3);
    $maxTokens = (int)($config['ai_max_tokens'] ?? 400);

    $payload = json_encode([
        'model' => $model,
        'messages' => $messagesForAI,
        'temperature' => $temperature,
        'max_tokens' => $maxTokens,
        'presence_penalty' => 0.1,
        'frequency_penalty' => 0.1,
    ], JSON_UNESCAPED_UNICODE);

    $ch = curl_init('https://api.openai.com/v1/chat/completions');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . getOpenAIToken(),
        ],
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['api_timeout'],
        CURLOPT_SSL_VERIFYPEER => true
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    if (curl_errno($ch)) {
        logMessage('cURL error: ' . curl_error($ch), 'API');
        curl_close($ch);
        return null;
    }

    curl_close($ch);

    if ($httpCode !== 200) {
        logMessage("API error $httpCode: $response", 'API');
        return null;
    }

    $data = json_decode($response, true);
    $content = $data['choices'][0]['message']['content'] ?? null;
    if ($content !== null) {
        @file_put_contents($cacheFile, json_encode(['content' => $content, 'ts' => time()], JSON_UNESCAPED_UNICODE));
    }
    return $content;
}

// ======== WhatsApp Media Helpers ========
function getWhatsAppMediaUrl(string $mediaId): ?array {
    global $config;
    if (!$mediaId) return null;
    $url = "https://graph.facebook.com/v19.0/{$mediaId}";
    $qs = http_build_query(['access_token' => $config['whatsapp_token']]);
    $ch = curl_init("{$url}?{$qs}");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['whatsapp_timeout'] ?? 25,
        CURLOPT_SSL_VERIFYPEER => true,
    ]);
    $res = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($code !== 200) return null;
    $json = json_decode($res, true);
    if (empty($json['url'])) return null;
    return ['url' => $json['url'], 'mime' => $json['mime_type'] ?? 'application/octet-stream'];
}

function downloadWhatsAppMedia(string $mediaId): ?array {
    global $config;
    $info = getWhatsAppMediaUrl($mediaId);
    if (!$info) return null;
    $headers = [
        'Authorization: Bearer ' . $config['whatsapp_token']
    ];
    $ext = 'bin';
    if (!empty($info['mime']) && strpos($info['mime'], '/') !== false) {
        $parts = explode('/', $info['mime']);
        $ext = end($parts);
        $ext = preg_replace('/[^a-z0-9]/i', '', $ext) ?: 'bin';
    }
    if (!is_dir('imports')) @mkdir('imports', 0755, true);
    $dest = 'imports/' . $mediaId . '.' . $ext;

    $fp = fopen($dest, 'wb');
    if (!$fp) return null;

    $ch = curl_init($info['url']);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_TIMEOUT => $config['whatsapp_timeout'] ?? 25,
        CURLOPT_HTTPHEADER => $headers,
    ]);
    $data = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($code >= 200 && $code < 300 && $data !== false) {
        fwrite($fp, $data);
        fclose($fp);
        return ['path' => $dest, 'mime' => $info['mime'] ?? ''];
    }
    fclose($fp);
    @unlink($dest);
    return null;
}


// ======== WhatsApp Upload & Send Media (Forwarding) ========
function uploadWhatsAppMedia(string $filePath, string $mimeType = ''): ?string {
    global $config;

    if (!is_file($filePath)) return null;

    $phoneId = $config['whatsapp_phone_id'] ?? $config['phone_number_id'] ?? '';
    $token   = $config['whatsapp_token'] ?? '';
    if (!$phoneId || !$token) return null;

    $apiVersion = $config['meta_api_version'] ?? 'v19.0';
    $url = "https://graph.facebook.com/{$apiVersion}/{$phoneId}/media";

    if ($mimeType === '') {
        $mimeType = @mime_content_type($filePath) ?: 'application/octet-stream';
    }

    $postFields = [
        'messaging_product' => 'whatsapp',
        'file' => new CURLFile($filePath, $mimeType, basename($filePath)),
    ];

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['whatsapp_timeout'] ?? 25,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $token
        ],
        CURLOPT_POSTFIELDS => $postFields,
    ]);

    $res = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if (curl_errno($ch)) {
        logMessage('WhatsApp upload cURL error: ' . curl_error($ch), 'ERROR');
    }
    curl_close($ch);

    if ($code < 200 || $code >= 300) {
        logMessage("WhatsApp upload failed ($code): $res", 'ERROR');
        return null;
    }

    $json = json_decode((string)$res, true);
    return $json['id'] ?? null;
}

function sendWhatsAppMediaById(string $to, string $mediaId, string $type, string $caption = '', string $filename = ''): bool {
    global $config;

    $phoneId = $config['whatsapp_phone_id'] ?? $config['phone_number_id'] ?? '';
    $token   = $config['whatsapp_token'] ?? '';
    if (!$phoneId || !$token) return false;

    $apiVersion = $config['meta_api_version'] ?? 'v19.0';
    $url = "https://graph.facebook.com/{$apiVersion}/{$phoneId}/messages";

    $payload = [
        'messaging_product' => 'whatsapp',
        'to' => $to,
        'type' => $type,
    ];

    if ($type === 'image') {
        $payload['image'] = array_filter([
            'id' => $mediaId,
            'caption' => $caption ?: null,
        ], fn($v) => $v !== null && $v !== '');
    } elseif ($type === 'video') {
        $payload['video'] = array_filter([
            'id' => $mediaId,
            'caption' => $caption ?: null,
        ], fn($v) => $v !== null && $v !== '');
    } elseif ($type === 'audio') {
        $payload['audio'] = ['id' => $mediaId];
    } else {
        // document
        $payload['type'] = 'document';
        $payload['document'] = array_filter([
            'id' => $mediaId,
            'caption' => $caption ?: null,
            'filename' => $filename ?: null,
        ], fn($v) => $v !== null && $v !== '');
    }

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['whatsapp_timeout'] ?? 25,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json'
        ],
        CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
    ]);

    $res = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if (curl_errno($ch)) {
        logMessage('WhatsApp send media cURL error: ' . curl_error($ch), 'ERROR');
    }
    curl_close($ch);

    if ($code < 200 || $code >= 300) {
        logMessage("WhatsApp send media failed ($code): $res", 'ERROR');
        return false;
    }

    return true;
}

function sendWhatsAppMediaByPath(string $to, string $filePath, string $mimeType = '', string $caption = ''): bool {
    global $config;
    if (!is_file($filePath)) return false;

    $mimeType = $mimeType ?: (@mime_content_type($filePath) ?: 'application/octet-stream');

    // Decide message type based on MIME
    $type = 'document';
    if (stripos($mimeType, 'image/') === 0) $type = 'image';
    elseif (stripos($mimeType, 'video/') === 0) $type = 'video';
    elseif (stripos($mimeType, 'audio/') === 0) $type = 'audio';

    $uploadedId = uploadWhatsAppMedia($filePath, $mimeType);
    if (!$uploadedId) return false;

    $filename = basename($filePath);
    $directOk = sendWhatsAppMediaById($to, $uploadedId, $type, $caption, $filename);
    if ($directOk) return true;

    if (!in_array($type, ['image', 'video', 'document'], true)) {
        return false;
    }

    // Fallback to approved media template when outside 24h window.
    $templateName = trim((string)($config['whatsapp_media_alert_template'] ?? ''));
    if ($templateName === '') {
        return false;
    }

    $headerType = in_array($type, ['image', 'video', 'document'], true) ? $type : 'document';
    $lang = (string)($config['template_language'] ?? 'ar');
    $safeCaption = trim($caption);
    if ($safeCaption === '') {
        $safeCaption = 'معاينة جديدة من العميل';
    }
    if (mb_strlen($safeCaption, 'UTF-8') > 900) {
        $safeCaption = mb_substr($safeCaption, 0, 900, 'UTF-8');
    }

    $headerPayload = ['id' => $uploadedId];
    if ($headerType === 'document' && $filename !== '') {
        $headerPayload['filename'] = $filename;
    }

    $components = [
        [
            'type' => 'header',
            'parameters' => [
                [
                    'type' => $headerType,
                    $headerType => $headerPayload,
                ],
            ],
        ],
        [
            'type' => 'body',
            'parameters' => [
                ['type' => 'text', 'text' => $safeCaption]
            ],
        ],
    ];

    return sendWhatsAppTemplate($to, $templateName, $components, $lang);
}

// ======== Admin Notifications & Media Forwarding ========
function notifyAdminOnInteraction(array $payload): void {
    global $config;

    if (!($config['notify_admin_on_every_interaction'] ?? true)) return;

    $admins = $config['admin_notify_numbers'] ?? [];
    if (empty($admins)) {
        $single = $config['admin_notify_number'] ?? ($config['sales_manager_number'] ?? '');
        if ($single) $admins = [$single];
    }
    $admins = array_values(array_filter(array_map('trim', $admins)));
    if (empty($admins)) return;

    $event = (string)($payload['event'] ?? 'message');
    $platform = (string)($payload['platform'] ?? '');
    $from = (string)($payload['from'] ?? '');
    $customerId = (string)($payload['customer_id'] ?? '');
    $waPhone = (string)($payload['wa_phone'] ?? '');
    $text = (string)($payload['text'] ?? '');
    $hasMedia = (bool)($payload['has_media'] ?? false);
    $customer = (isset($payload['customer']) && is_array($payload['customer'])) ? $payload['customer'] : [];
    $salesSummary = buildSalesSummaryLine($customer);

    // Rate limit per (event+platform+customer key) to avoid spam
    $rateSec = (int)($config['admin_notify_rate_limit_seconds'] ?? 60);
    if ($rateSec < 0) $rateSec = 0;

    $rateKeySource = $customerId !== '' ? $customerId : $from;
    $key = md5($event . '|' . $platform . '|' . $rateKeySource);
    $flag = "cache/admin_notify_{$key}.json";
    $now = time();
    $last = 0;
    $lastHash = '';
    if (is_file($flag)) {
        $j = json_decode((string)@file_get_contents($flag), true);
        $last = (int)($j['ts'] ?? 0);
        $lastHash = (string)($j['h'] ?? '');
    }
    $curHash = md5($text . '|' . ($hasMedia ? '1':'0') . '|' . $salesSummary);
    if (($now - $last) < $rateSec && $curHash === $lastHash) {
        return;
    }
    @file_put_contents($flag, json_encode(['ts' => $now, 'h' => $curHash], JSON_UNESCAPED_UNICODE));

    $fromName = trim((string)($payload['from_name'] ?? ''));
    $displayFrom = $fromName ? "{$fromName} ({$from})" : $from;

    $date = date('Y-m-d H:i:s');
    $shortText = trim($text);
    if (mb_strlen($shortText) > 500) $shortText = mb_substr($shortText, 0, 480) . '...';

    $title = $event === 'comment' ? "💬 تعليق جديد" : ($hasMedia ? "📎 تفاعل + ميديا" : "💬 رسالة جديدة");
    $msg = "{$title}
"
         . "المنصة: {$platform}
"
         . "العميل: {$displayFrom}
"
         . (($customerId !== '' && $customerId !== $from) ? ("CRM ID: {$customerId}
") : '')
         . ($waPhone !== '' ? ("WA: {$waPhone}
") : '')
         . ($event === 'comment' ? ("ID التعليق: " . ($payload['comment_id'] ?? '') . "
") : '')
         . ($salesSummary !== '' ? ("بيانات البيع: {$salesSummary}
") : '')
         . "النص: {$shortText}
"
         . "الوقت: {$date}";

    foreach ($admins as $admin) {
        sendInternalAlert($admin, $msg);
    }
}

function forwardIncomingMediaToAdmin(array $mediaFiles, array $ctx): void {
    global $config;

    // Always forward previews to the preview WhatsApp number (falls back to admin number).
    $target = $config['preview_whatsapp_number'] ?? ($config['admin_notify_number'] ?? ($config['sales_manager_number'] ?? ''));
    $admin = preg_replace('/\D+/', '', (string)$target);
    if ($admin === '') {
        $admin = '201094945225';
    }

    $typesAllowed = $config['forward_media_types'] ?? ['image'];
    if (!is_array($typesAllowed) || empty($typesAllowed)) $typesAllowed = ['image'];

    $platform = (string)($ctx['platform'] ?? '');
    $from = (string)($ctx['from'] ?? '');
    $customerId = (string)($ctx['customer_id'] ?? '');
    $waPhone = (string)($ctx['wa_phone'] ?? '');
    $fromName = trim((string)($ctx['from_name'] ?? ''));
    $text = trim((string)($ctx['text'] ?? ''));
    $intent = (string)($ctx['intent'] ?? '');
    $customer = (isset($ctx['customer']) && is_array($ctx['customer'])) ? $ctx['customer'] : [];
    $publicBase = resolvePublicBaseUrl();
    $crmUrl = $publicBase !== '' ? rtrim($publicBase, '/') . '/customers/index.php' : '';
    $intentLabels = [
        'pricing' => 'استفسار سعر',
        'contact' => 'طلب تواصل',
        'site_visit' => 'طلب معاينة',
        'maintenance' => 'صيانة',
        'pool' => 'حمام سباحة',
        'water_features' => 'شلالات/نافورات',
        'landscape' => 'لاندسكيب',
        'finishing' => 'تشطيبات',
        'design' => 'تصميم',
        'preview_media' => 'إرسال معاينة',
        'service_from_profile' => 'خدمة محددة',
        'wa_capture_pending' => 'في انتظار رقم واتساب',
        'wa_phone_linked' => 'تم ربط رقم واتساب',
    ];

    foreach ($mediaFiles as $mf) {
        $type = (string)($mf['type'] ?? '');
        $note = (string)($mf['note'] ?? '');
        $url = trim((string)($mf['url'] ?? ''));
        $publicUrl = trim((string)($mf['public_url'] ?? ''));
        $path = trim((string)($mf['path'] ?? ''));

        // Skip extracted frames by default (to avoid duplicates)
        if ($note === 'extracted_video_frame' && !($config['forward_video_frames_to_admin'] ?? false)) {
            continue;
        }

        if (!in_array($type, $typesAllowed, true)) continue;

        $city = trim((string)($customer['city'] ?? ($customer['project_city'] ?? '')));
        $placeType = trim((string)($customer['place_type'] ?? ($customer['project_place_type'] ?? '')));
        $services = customerServicesText($customer);
        $scope = trim((string)($customer['project_scope'] ?? ''));
        $budget = trim((string)($customer['budget_range'] ?? ''));
        $timeline = trim((string)($customer['project_timeline'] ?? ''));
        $customerRef = $waPhone !== '' ? $waPhone : ($customerId !== '' ? $customerId : $from);
        $customerLabel = $fromName !== '' ? ($fromName . ' - ' . $customerRef) : $customerRef;
        if ($publicUrl === '' && $url !== '') {
            $publicUrl = $url;
        }
        if ($publicUrl === '' && $path !== '' && strpos(str_replace('\\', '/', $path), 'customers_media/') === 0) {
            $publicUrl = buildPublicUrlFromRelativePath($path);
        }

        $locationText = trim($city . ($placeType !== '' ? (' - ' . $placeType) : ''));
        if ($locationText === '') $locationText = 'غير محدد';

        $serviceText = $services;
        if ($serviceText === '' && $intent !== '' && $intent !== 'unknown') {
            $serviceText = ($intentLabels[$intent] ?? $intent);
        }
        if ($serviceText === '') $serviceText = 'غير محدد';

        $msg = "📸 معاينة جديدة\n"
             . "👤 العميل: {$customerLabel}\n"
             . "🌐 المنصة: {$platform}\n"
             . "📍 المكان: {$locationText}\n"
             . "🏗️ الخدمة/النية: {$serviceText}\n";
        if ($scope !== '') {
            $msg .= "🧭 نوع الطلب: {$scope}\n";
        }
        if ($budget !== '') {
            $msg .= "💰 الميزانية: {$budget}\n";
        }
        if ($timeline !== '') {
            $msg .= "⏱️ وقت التنفيذ: {$timeline}\n";
        }
        if ($customerId !== '') {
            $msg .= "🆔 CRM ID: {$customerId}\n";
        }

        if ($text !== '') {
            $msg .= "📝 ملاحظة: " . (mb_strlen($text) > 160 ? mb_substr($text, 0, 160) . '...' : $text) . "\n";
        }
        if ($publicUrl !== '') {
            $msg .= "🖼️ لينك المعاينة: {$publicUrl}\n";
        } else {
            $msg .= "🖼️ لينك المعاينة: غير متاح (الملف محفوظ محليًا)\n";
        }
        if ($crmUrl !== '') {
            $msg .= "🗂️ CRM: {$crmUrl}\n";
        }
        $msg .= "🔎 ابحث برقم العميل: {$customerRef}";

        sendInternalAlert($admin, $msg);
    }
}

// ======== Comment Replies (Template Mode) ========
function buildCommentReplies(string $commentText, string $platform): array {
    global $config;
    $previewNumber = (string)($config['preview_number_display'] ?? '+20 10 94945225');

    $public = "يسعدنا خدمتك. بعتنالك رسالة خاصة لمتابعة التفاصيل على {$previewNumber}.";
    // Private message: one clear CTA + one question
    $private = "أهلاً بيك 🙌
"
             . "علشان نقدر نساعدك بسرعة، ابعت المعاينة/المقاس/المكان على الرقم التالي: {$previewNumber}
"
             . "تحب الخدمة تكون (تصميم جديد) ولا (صيانة/تشطيب)؟";

    // Keep IG public replies short
    if ($platform === 'instagram' && mb_strlen($public) > 180) {
        $public = "بعتنالك رسالة خاصة لمتابعة المعاينة على {$previewNumber}.";
    }

    return ['public' => $public, 'private' => $private];
}

// ======== Facebook/Instagram Private Replies ========
function sendFacebookPrivateReply(string $commentId, string $text, string $pageAccessToken): bool {
    global $config;

    $commentId = trim($commentId);
    if ($commentId === '' || trim($text) === '' || trim($pageAccessToken) === '') return false;

    $apiVersion = $config['meta_api_version'] ?? 'v19.0';
    $url = "https://graph.facebook.com/{$apiVersion}/me/messages?access_token=" . urlencode($pageAccessToken);

    $payload = [
        'recipient' => ['comment_id' => $commentId],
        'message' => ['text' => $text],
        'messaging_type' => 'RESPONSE'
    ];

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['messenger_timeout'] ?? 8,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
    ]);
    $res = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if (curl_errno($ch)) {
        logMessage('FB private reply cURL error: ' . curl_error($ch), 'ERROR');
    }
    curl_close($ch);

    if ($code < 200 || $code >= 300) {
        logMessage("FB private reply failed ($code): $res", 'ERROR');
        return false;
    }
    return true;
}

function sendInstagramPrivateReply(string $commentId, string $text, string $accessToken): bool {
    global $config;

    $commentId = trim($commentId);
    if ($commentId === '' || trim($text) === '' || trim($accessToken) === '') return false;

    $apiVersion = $config['meta_api_version'] ?? 'v19.0';

    // Preferred endpoint per IG private reply spec: /{ig-user-id}/messages (if configured)
    $igUserId = trim((string)($config['ig_user_id'] ?? ''));
    $endpoints = [];
    if ($igUserId !== '') {
        $endpoints[] = "https://graph.facebook.com/{$apiVersion}/{$igUserId}/messages";
    }
    // Fallback (some setups accept Send API on /me/messages for IG messaging)
    $endpoints[] = "https://graph.facebook.com/{$apiVersion}/me/messages";

    $payload = [
        'recipient' => ['comment_id' => $commentId],
        'message' => ['text' => $text],
        'messaging_type' => 'RESPONSE'
    ];

    foreach ($endpoints as $base) {
        $url = $base . '?access_token=' . urlencode($accessToken);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => $config['messenger_timeout'] ?? 8,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
            CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
        ]);

        $res = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($code >= 200 && $code < 300) return true;

        logMessage("IG private reply failed on {$base} ($code): {$res}", 'WARNING');
    }

    return false;
}


function transcribeAudioOpenAI(string $filePath): ?string {
    global $config;
    if (!is_file($filePath)) return null;
    $model = $config['ai_transcribe_model'] ?? 'whisper-1';

    $ch = curl_init('https://api.openai.com/v1/audio/transcriptions');

    $fields = [
        'model' => $model,
        'file' => new CURLFile($filePath)
    ];

    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getOpenAIToken()
        ],
        CURLOPT_POSTFIELDS => $fields,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['api_timeout'] ?? 30,
        CURLOPT_SSL_VERIFYPEER => true,
    ]);
    $res = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($code !== 200) return null;
    $json = json_decode($res, true);
    return $json['text'] ?? null;
}
function buildSystemPrompt(array $customer): string {
    global $config;

    $businessType = $customer['business_type'] ?? '';
    $interests = !empty($customer['interests']) && is_array($customer['interests']) ? implode('، ', $customer['interests']) : '';
    $status = $customer['qualification_status'] ?? 'new';

    $websiteBase = rtrim($config['company_website'] ?? 'https://paradisegroup-eg.com', '/');
    $previewNumber = (string)($config['preview_number_display'] ?? '+20 10 94945225');

    $contextLine = trim("نوع النشاط: {$businessType} | الاهتمامات: {$interests} | الحالة: {$status}");
    if ($contextLine === "نوع النشاط:  | الاهتمامات:  | الحالة: {$status}") {
        $contextLine = "الحالة: {$status}";
    }

    return <<<PROMPT
{$contextLine}

أنت ممثل خدمة عملاء ومبيعات لشركة **Paradise Group** في مصر (حمامات سباحة وخدمات مرتبطة).

قواعد إلزامية:
- طابق لغة العميل دائمًا: عربي = لهجة مصرية واضحة، English = professional simple English.
- رد **بسرعة** وبأسلوب مبيعات احترافي يركز على الفائدة والـ next step.
- جُمل قصيرة وواضحة.
- **سؤال واحد فقط** في كل رسالة (الأهم).
- في أول رد لأي عميل جديد: ابدأ بترحيب واضح ثم اسأل عن المشكلة/الطلب.
- **ممنوع ذكر أي أسعار/نطاقات/تقديرات**. لو العميل سأل عن السعر: اطلب (المقاس/المكان/نوع الخدمة) ووجّهه لإرسال المعاينة.
- سيصلك داخل المحادثة مقتطف باسم **WEBSITE FACTS** من الموقع الرسمي: {$websiteBase}. اعتمد عليه فقط.
- لما العميل يطلب معلومات خدمة: قدّم له أهم المعلومات المتاحة من WEBSITE FACTS بشكل عملي.
- لو المعلومة مش موجودة في WEBSITE FACTS: قول "مش متأكد 100%" ووجّه للتواصل على واتساب/اتصال.
- اختم كل رد قابل للبيع بـ CTA واحدة واضحة (إرسال معاينة، تحديد مدينة، أو ترتيب معاينة ميدانية).

عند اتضاح نية العميل:
- اعرض خطوة المعاينة بهذا النص: **📸 لإرسال المعاينة على الرقم التالي: {$previewNumber}**
PROMPT;
}


/**
 * Sends an interactive message with up to 3 buttons.

 */
function sendInteractiveMessage(string $to, string $body, array $buttons): bool {
    global $config;

    $buttonPayloads = [];
    foreach (array_slice($buttons, 0, 3) as $i => $buttonTitle) {
        $buttonPayloads[] = [
            'type' => 'reply',
            'reply' => [
                'id' => 'btn' . ($i+1), // لازم id بسيط جدًا
                'title' => $buttonTitle
            ]
        ];
    }

    $payload = json_encode([
        'messaging_product' => 'whatsapp',
        'to' => $to,
        'type' => 'interactive',
        'interactive' => [
            'type' => 'button',
            'body' => ['text' => $body],
            'action' => ['buttons' => $buttonPayloads]
        ]
    ], JSON_UNESCAPED_UNICODE);

    $ch = curl_init("https://graph.facebook.com/v19.0/{$config['phone_number_id']}/messages");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $config['whatsapp_token'],
        ],
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['whatsapp_timeout'],
        CURLOPT_SSL_VERIFYPEER => true
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    logMessage("WhatsApp API response: $httpCode $response", 'INFO', $to);

    if (curl_errno($ch)) {
        logMessage('WhatsApp Interactive error: ' . curl_error($ch), 'ERROR', $to);
        curl_close($ch);
        return false;
    }
    curl_close($ch);
    return $httpCode >= 200 && $httpCode < 300;
}



function notifySalesTeam(string $phone, array $customer, string $signalType): void {
    // Broadcast to all configured sales numbers (new behavior)
    global $config;
    $msg = "إشعار مبيعات ($signalType): عميل مهتم $phone\n" .
           "الحالة: " . ($customer['qualification_status'] ?? 'new') . " | النقاط: " . ($customer['lead_score'] ?? 0);
    $numbers = $config['sales_notify_numbers'] ?? [];
    if (is_string($numbers)) { $numbers = [$numbers]; }
    foreach ($numbers as $n) { @sendInternalAlert($n, $msg); }
    $manager = $config['sales_manager_number'] ?? '201094945225';
    if ($manager) { @sendInternalAlert($manager, "[تنبيه مدير المبيعات] $msg"); }
    logMessage("Sales Team notified for $signalType signal from $phone", 'CONVERSION', $phone);
    return;
    $msg = "عميل مؤهل ($signalType): رقم الهاتف $phone، حالة العميل: {$customer['qualification_status']}، نقاط: {$customer['lead_score']}";

    // إرسال تنبيه لـ WhatsApp الخاص بفريق المبيعات
    sendWhatsAppMessage("201094945225", $msg);

    // حفظ السجل
    logMessage("Sales Team notified for $signalType signal from $phone", 'CONVERSION', $phone);
}


// Send a sales notification when a new chat starts
function notifySalesOnNewChat(string $phone, string $platform, array $customer): void {
    global $config;

    // Avoid duplicate notifications if already sent
    if (!empty($customer['first_chat_notified'])) {
        return;
    }

    $numbers = $config['sales_notify_numbers'] ?? [];
    if (is_string($numbers)) { $numbers = [$numbers]; }

    $leadScore = (string)($customer['lead_score'] ?? 0);
    $qStatus = (string)($customer['qualification_status'] ?? 'new');
    $ts = date('Y-m-d H:i');

    $msg = "إشعار مبيعات: بدأ عميل محادثة جديدة\n" .
           "الهاتف: +$phone\n" .
           "المنصة: $platform\n" .
           "الحالة: $qStatus | النقاط: $leadScore\n" .
           "الوقت: $ts";

    foreach ($numbers as $n) {
        @sendInternalAlert($n, $msg);
    }
    // Extra emphasized copy to Sales Manager
    $manager = $config['sales_manager_number'] ?? '201094945225';
    if ($manager && !in_array($manager, $numbers, true)) {
        @sendInternalAlert($manager, "[تنبيه مدير المبيعات] $msg");
    } else if ($manager) {
        @sendInternalAlert($manager, "[تنبيه إضافي] $msg");
    }

    // Mark as notified and persist
    $customer['first_chat_notified'] = time();
    saveCustomerData($phone, $customer);

    logMessage("Sales notified for new chat", 'CONVERSION', $phone);
}



// ======== WhatsApp Integration ========
function sendWhatsAppMessage(string $to, string $message): bool {
    global $config;
    
    if (mb_strlen($message, 'UTF-8') > 1000) {
        $message = mb_substr($message, 0, 1000, 'UTF-8') . '...';
    }
    
    $payload = json_encode([
        'messaging_product' => 'whatsapp',
        'to' => $to,
        'type' => 'text',
        'text' => ['body' => $message]
    ], JSON_UNESCAPED_UNICODE);
    
    $ch = curl_init("https://graph.facebook.com/v19.0/{$config['phone_number_id']}/messages");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $config['whatsapp_token'],
        ],
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['whatsapp_timeout'],
        CURLOPT_SSL_VERIFYPEER => true
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    // Log API response for diagnostics
    logMessage("WhatsApp API response (text): $httpCode $response", 'API', $to);

    if (curl_errno($ch)) {
        logMessage('WhatsApp error: ' . curl_error($ch), 'ERROR', $to);
        curl_close($ch);
        return false;
    }
    
    curl_close($ch);
    return $httpCode >= 200 && $httpCode < 300;
}

// Sends a WhatsApp template message (fallback for outside 24h window)
function sendWhatsAppTemplate(string $to, string $templateName, array $components = [], string $lang = 'ar'): bool {
    global $config;
    $payload = [
        'messaging_product' => 'whatsapp',
        'to' => $to,
        'type' => 'template',
        'template' => [
            'name' => $templateName,
            'language' => ['code' => $lang],
        ]
    ];
    if (!empty($components)) {
        $payload['template']['components'] = $components;
    }
    $json = json_encode($payload, JSON_UNESCAPED_UNICODE);
    $ch = curl_init("https://graph.facebook.com/v19.0/{$config['phone_number_id']}/messages");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $config['whatsapp_token'],
        ],
        CURLOPT_POSTFIELDS => $json,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $config['whatsapp_timeout'],
        CURLOPT_SSL_VERIFYPEER => true
    ]);
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    logMessage("WhatsApp API response (template): $httpCode $response", 'API', $to);
    if (curl_errno($ch)) {
        logMessage('WhatsApp template error: ' . curl_error($ch), 'ERROR', $to);
        curl_close($ch);
        return false;
    }
    curl_close($ch);
    return $httpCode >= 200 && $httpCode < 300;
}

// Helper to send alerts to internal team with template fallback
function sendInternalAlert(string $to, string $message): bool {
    global $config;
    $ok = sendWhatsAppMessage($to, $message);
    if ($ok) return true;
    $tpl = trim((string)($config['whatsapp_alert_template'] ?? ''));
    if ($tpl === '') return false;
    $lang = (string)($config['template_language'] ?? 'ar');
    $safeMessage = trim($message);
    if (mb_strlen($safeMessage, 'UTF-8') > 900) {
        $safeMessage = mb_substr($safeMessage, 0, 900, 'UTF-8');
    }
    // Single body parameter with full alert text
    $components = [
        [
            'type' => 'body',
            'parameters' => [ ['type' => 'text', 'text' => $safeMessage] ]
        ]
    ];
    return sendWhatsAppTemplate($to, $tpl, $components, $lang);
}

// ======== Error Handling ========
function handleError(Throwable $e, ?string $from, ?string $userInput): void {
    $errorMsg = $e->getMessage();
    $traceId = uniqid('err_');
    
    logMessage("Error [$traceId]: $errorMsg", 'ERROR', $from);
    
    if ($from && preg_match('/^\d{10,15}$/', $from)) {
        $errorResponse = 'شكراً لاهتمامك! سيتم التواصل معك قريباً من فريق برادايس جروب. للاستفسار الفوري: +20 10 94945225';
        sendWhatsAppMessage($from, $errorResponse);
        
        if ($userInput) {
            $meta = [
                'source' => 'error_handler',
                'trace_id' => $traceId,
            ];
            addToChatLog($from, $userInput, 'user', $meta);
            addToChatLog($from, $errorResponse, 'assistant');
        }
        
        $customer = loadCustomerData($from);
        $customer['last_error'] = [
            'message' => $errorMsg,
            'trace_id' => $traceId,
            'timestamp' => time()
        ];
        saveCustomerData($from, $customer);
    }
    
    http_response_code(500);
    echo json_encode(['error' => 'Internal error', 'trace_id' => $traceId]);
}

// ======== File Maintenance ========
function cleanupOldFiles(): void {
    $thirtyDaysAgo = time() - (30 * 86400);
    
    // تنظيف ملفات الـ rate limit
    foreach (glob('logs/rate_*.txt') as $file) {
        if (@filemtime($file) < $thirtyDaysAgo) @unlink($file);
    }
    
    // تنظيف ملفات الـ cache (بعد ساعة واحدة)
    $oneHourAgo = time() - 3600;
    foreach (glob('cache/comment_processed_*.txt') as $file) {
        if (@filemtime($file) < $oneHourAgo) @unlink($file);
    }
}



function replyToComment($commentId, $replyText, $accessToken, $platform = 'facebook') {
    if (empty($commentId) || empty($replyText) || empty($accessToken)) {
        logMessage("Reply failed: Missing parameters", 'ERROR');
        return false;
    }
    
    // جرب طرق مختلفة للرد
    $urls = [];
    
    if ($platform === 'instagram') {
        $urls[] = "https://graph.facebook.com/v19.0/{$commentId}/replies";
        $urls[] = "https://graph.facebook.com/v19.0/{$commentId}/comments";
    } else {
        $urls[] = "https://graph.facebook.com/v19.0/{$commentId}/comments";
        
        // إذا كان المعرف مُركب، جرب الجزء الثاني
        if (strpos($commentId, '_') !== false) {
            $parts = explode('_', $commentId);
            $simpleId = end($parts);
            $urls[] = "https://graph.facebook.com/v19.0/{$simpleId}/comments";
        }
    }
    
    foreach ($urls as $url) {
        $success = attemptCommentReply($url, $replyText, $accessToken);
        if ($success) {
            logMessage("Comment reply successful with URL: $url", 'INFO');
            return true;
        }
    }
    
    logMessage("All reply attempts failed for comment: $commentId", 'ERROR');
    return false;
}

function attemptCommentReply($url, $replyText, $accessToken) {
    $data = [
        'message' => $replyText,
        'access_token' => $accessToken
    ];
    
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($data),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/x-www-form-urlencoded',
            'User-Agent: SmartLogicBot/1.0'
        ]
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    // Log محاولة الرد
    file_put_contents('logs/comment_reply_debug.log', 
        date('Y-m-d H:i:s') . " Reply attempt\n" .
        "URL: $url\n" .
        "HTTP: $httpCode\n" .
        "Response: $response\n\n", 
        FILE_APPEND
    );
    
    curl_close($ch);
    
    return $httpCode >= 200 && $httpCode < 300;
}






function likeComment($commentId, $accessToken) {
    if (empty($commentId) || empty($accessToken)) {
        return false;
    }
    
    // محاولة عمل لايك مع طرق مختلفة
    $attempts = [
        $commentId,  // المعرف الكامل
        explode('_', $commentId)[1] ?? $commentId  // الجزء الثاني إذا كان مُركب
    ];
    
    foreach ($attempts as $id) {
        if (empty($id)) continue;
        
        $success = attemptCommentLike($id, $accessToken);
        if ($success) {
            logMessage("Comment liked successfully with ID: $id", 'INFO');
            return true;
        }
    }
    
    logMessage("All like attempts failed for comment: $commentId", 'WARNING');
    return false;
}

function attemptCommentLike($commentId, $accessToken) {
    $url = "https://graph.facebook.com/v19.0/{$commentId}/likes";
    
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/x-www-form-urlencoded',
            'User-Agent: SmartLogicBot/1.0'
        ],
        CURLOPT_POSTFIELDS => "access_token=" . urlencode($accessToken),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_TIMEOUT => 10
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    // Log محاولة اللايك
    file_put_contents('logs/like_attempts.log', 
        date('Y-m-d H:i:s') . " Like attempt: $commentId, HTTP: $httpCode\n" .
        "URL: $url\n" .
        "Response: $response\n\n", 
        FILE_APPEND
    );
    
    curl_close($ch);
    
    return $httpCode >= 200 && $httpCode < 300;
}









function sendInstagramDM($userId, $message, $accessToken) {
    global $config;
    $token = trim((string)$accessToken);
    if (trim((string)$userId) === '' || $token === '') {
        logMessage('Instagram DM skipped: missing recipient or token', 'WARNING', (string)$userId);
        return false;
    }

    // Instagram DMs تستخدم نفس API الـ Messenger
    $apiVersion = $config['meta_api_version'] ?? 'v19.0';
    $url = "https://graph.facebook.com/{$apiVersion}/me/messages";
    
    $payload = json_encode([
        'recipient' => ['id' => $userId],
        'message'   => ['text' => $message],
        'messaging_type' => 'RESPONSE' // مهم للـ Instagram
    ], JSON_UNESCAPED_UNICODE);
    
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $token
        ],
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_USERAGENT => 'SmartLogicBot/1.0'
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    logMessage("Instagram DM API response: $httpCode $response", 'INFO', $userId);
    
    curl_close($ch);
    return $httpCode >= 200 && $httpCode < 300;
}




function fetchCommentText($commentId, $accessToken) {
    if (empty($commentId) || empty($accessToken)) {
        return '';
    }
    
    // جرب fields مختلفة للحصول على النص
    $fields = ['message', 'text', 'from', 'created_time'];
    $url = "https://graph.facebook.com/v19.0/$commentId?" . 
           "fields=" . implode(',', $fields) . 
           "&access_token=$accessToken";
    
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_USERAGENT => 'SmartLogicBot/1.0'
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    // Log الاستجابة للتشخيص
    file_put_contents('logs/fetch_comment.log', 
        date('Y-m-d H:i:s') . " Comment ID: $commentId, HTTP: $httpCode\n" .
        "Response: $response\n\n", 
        FILE_APPEND
    );
    
    curl_close($ch);
    
    // إذا فشل الـ API، ارجع نص فارغ بدلاً من خطأ
    if ($httpCode !== 200) {
        logMessage("Failed to fetch comment $commentId: HTTP $httpCode - trying to proceed without API text", 'WARNING');
        return '';
    }
    
    $json = json_decode($response, true);
    return $json['message'] ?? $json['text'] ?? '';
}




function postReplyToPost($postId, $replyText, $accessToken) {
    if (empty($postId) || empty($replyText) || empty($accessToken)) {
        return false;
    }
    
    // إذا فشل الرد على التعليق، جرب إنشاء comment جديد على الـ post
    $url = "https://graph.facebook.com/v19.0/{$postId}/comments";
    
    $data = [
        'message' => $replyText,
        'access_token' => $accessToken
    ];
    
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($data),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_TIMEOUT => 15
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    // Log محاولة الرد على الـ post
    file_put_contents('logs/post_reply_attempts.log', 
        date('Y-m-d H:i:s') . " Attempting post reply to: $postId, HTTP: $httpCode\n" .
        "Response: $response\n\n", 
        FILE_APPEND
    );
    
    curl_close($ch);
    
    return $httpCode >= 200 && $httpCode < 300;
}



function createReplyWithMention(string $replyText, string $userId, string $platform, string $fromUsername = ''): string {
    $replyText = trim($replyText);
    $prefix = '';

    if ($platform === 'facebook' && trim($userId) !== '') {
        $prefix = '@[' . trim($userId) . ']';
    } elseif ($platform === 'instagram') {
        $username = ltrim(trim($fromUsername), '@');
        if ($username !== '') {
            $prefix = '@' . $username;
        }
    }

    if ($prefix !== '' && $replyText !== '') {
        return $prefix . ' ' . $replyText;
    }
    if ($prefix !== '') {
        return $prefix;
    }
    return $replyText;
}





if (mt_rand(1, 100) === 1) cleanupOldFiles();
