<?php
/**
 * Admin broadcast messages - v27.41 combined photo + caption + inline buttons + callback-safe buttons
 */
require_once 'config.php';

header('Content-Type: application/json; charset=utf-8');

function broadcast_json(array $data): void {
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function broadcast_html_message(string $raw): string {
    $raw = trim($raw);
    if ($raw === '') return '';

    // Escape everything first, then enable a small safe Markdown-like subset for admins.
    $text = htmlspecialchars($raw, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

    // **bold** => <b>bold</b>  (Telegram HTML parse_mode)
    $text = preg_replace('/\*\*([^\n*][\s\S]*?[^\n*]|[^\n*])\*\*/u', '<b>$1</b>', $text);

    // `code` => <code>code</code>
    $text = preg_replace('/`([^`]+)`/u', '<code>$1</code>', $text);

    return trim($text);
}


function broadcast_caption_plain_length(string $html): int {
    $plain = html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    return mb_strlen(trim($plain), 'UTF-8');
}

function broadcast_is_caption_too_long(string $html): bool {
    // Telegram Bot API photo captions are limited to 1024 visible characters after parsing entities.
    return $html !== '' && broadcast_caption_plain_length($html) > 1024;
}

function broadcast_mini_app_url(): string {
    $url = '';
    try { $url = trim((string)getSetting('mini_app_url', defined('MINIAPP_URL') ? MINIAPP_URL : '')); } catch (Throwable $e) { $url = ''; }
    if ($url === '') $url = defined('MINIAPP_URL') ? (string)MINIAPP_URL : ('https://' . DOMAIN . '/telegram-bot/public/mini_app/');
    return rtrim($url, '/') . '/';
}

function broadcast_button_definition(string $key): ?array {
    switch ($key) {
        case 'buy':
        case 'plans':
            return ['key' => 'buy', 'button' => ['text' => '🛒 خرید اشتراک', 'callback_data' => 'BUY']];
        case 'panel':
        case 'dash':
            return ['key' => 'panel', 'button' => ['text' => '📱 پنل Gem ACC', 'web_app' => ['url' => broadcast_mini_app_url()]]];
        case 'support':
        case 'sup':
            return ['key' => 'support', 'button' => ['text' => '💬 پشتیبانی', 'callback_data' => 'SUPPORT']];
        default:
            return null;
    }
}

function broadcast_normalize_button_keys($raw): array {
    if (is_string($raw)) {
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            $raw = $decoded;
        } elseif ($raw !== '') {
            $raw = preg_split('/[\s,؛،]+/u', $raw);
        } else {
            $raw = [];
        }
    }
    if (!is_array($raw)) return [];
    $out = [];
    $seen = [];
    foreach ($raw as $k) {
        $def = broadcast_button_definition(strtolower(trim((string)$k)));
        if (!$def) continue;
        $key = $def['key'];
        if (isset($seen[$key])) continue;
        $seen[$key] = true;
        $out[] = $key;
    }
    return $out;
}

function broadcast_reply_markup_from_keys(array $keys): ?array {
    $buttons = [];
    $seen = [];
    foreach ($keys as $key) {
        $def = broadcast_button_definition((string)$key);
        if (!$def) continue;
        $canonical = $def['key'];
        if (isset($seen[$canonical])) continue;
        $seen[$canonical] = true;
        $buttons[] = $def['button'];
    }
    if (!$buttons) return null;
    $rows = [];
    for ($i = 0; $i < count($buttons); $i += 2) {
        $rows[] = array_slice($buttons, $i, 2);
    }
    return ['inline_keyboard' => $rows];
}

function broadcast_plain_log_message(string $raw): string {
    $raw = trim($raw);
    return $raw === '' ? '[PHOTO]' : mb_substr($raw, 0, 5000, 'UTF-8');
}

function broadcast_extract_photo_file_id(array $result): string {
    if (empty($result['ok']) || empty($result['result']['photo']) || !is_array($result['result']['photo'])) return '';
    $photos = $result['result']['photo'];
    $last = end($photos);
    return is_array($last) ? (string)($last['file_id'] ?? '') : '';
}

function broadcast_count_result($result, int &$sent, int &$failed, int &$blocked): void {
    if ($result && !empty($result['ok'])) {
        $sent++;
        return;
    }
    $errorCode = is_array($result) ? (int)($result['error_code'] ?? 0) : 0;
    if (in_array($errorCode, [400, 403], true)) $blocked++;
    else $failed++;
}

$userId = miniapp_require_admin();

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    broadcast_json(['ok' => false, 'error' => 'POST required']);
}

$input = json_decode(file_get_contents('php://input'), true);
if (!is_array($input)) $input = $_POST;

$rawMessage = trim((string)($input['message'] ?? $_POST['message'] ?? ''));
$targetType = (string)($input['target_type'] ?? $_POST['target_type'] ?? 'all'); // all, active, inactive, self
$photoFileId = trim((string)($input['photo_file_id'] ?? $_POST['photo_file_id'] ?? ''));

$allowedTargets = ['all', 'active', 'inactive', 'self'];
if (!in_array($targetType, $allowedTargets, true)) $targetType = 'all';

// Handle uploaded image. Do NOT send it to admin just to get file_id; that was the cause of duplicate admin test images.
$uploadedPhotoPath = null;
if (isset($_FILES['image']) && is_array($_FILES['image']) && (int)$_FILES['image']['error'] === UPLOAD_ERR_OK) {
    $tmp = (string)$_FILES['image']['tmp_name'];
    $size = (int)($_FILES['image']['size'] ?? 0);
    if ($size <= 0 || $size > 10 * 1024 * 1024) {
        broadcast_json(['ok' => false, 'error' => 'حجم عکس نامعتبر است یا بیشتر از ۱۰ مگابایت است']);
    }
    $mime = function_exists('mime_content_type') ? (string)@mime_content_type($tmp) : '';
    if ($mime && strpos($mime, 'image/') !== 0) {
        broadcast_json(['ok' => false, 'error' => 'فقط فایل تصویری مجاز است']);
    }
    $uploadDir = __DIR__ . '/../../uploads/broadcasts/';
    if (!is_dir($uploadDir)) @mkdir($uploadDir, 0755, true);
    $ext = strtolower(pathinfo((string)($_FILES['image']['name'] ?? 'jpg'), PATHINFO_EXTENSION));
    if (!in_array($ext, ['jpg', 'jpeg', 'png', 'webp', 'gif'], true)) $ext = 'jpg';
    $uploadedPhotoPath = $uploadDir . uniqid('broadcast_', true) . '.' . $ext;
    if (!move_uploaded_file($tmp, $uploadedPhotoPath)) {
        broadcast_json(['ok' => false, 'error' => 'آپلود عکس ناموفق بود']);
    }
}

if ($rawMessage === '' && $photoFileId === '' && !$uploadedPhotoPath) {
    broadcast_json(['ok' => false, 'error' => 'پیام یا عکس الزامی است']);
}

// Process selectable inline buttons. Admin UI sends a JSON array in buttons[] order.
$selectedButtonKeys = broadcast_normalize_button_keys($input['buttons'] ?? $_POST['buttons'] ?? []);
$tokenButtonKeys = [];
$cleanMessage = preg_replace_callback('/\/(dash|plans|sup)\b/u', function($matches) use (&$tokenButtonKeys) {
    $tokenButtonKeys[] = strtolower((string)$matches[1]);
    return '';
}, $rawMessage);
$cleanMessage = trim((string)$cleanMessage);

// Keep backward compatibility with old text tokens while preferring explicit UI order first.
$buttonKeys = array_values(array_unique(array_merge($selectedButtonKeys, broadcast_normalize_button_keys($tokenButtonKeys))));
$replyMarkup = broadcast_reply_markup_from_keys($buttonKeys);
$htmlMessage = broadcast_html_message($cleanMessage);

if (($photoFileId !== '' || $uploadedPhotoPath) && broadcast_is_caption_too_long($htmlMessage)) {
    if ($uploadedPhotoPath && file_exists($uploadedPhotoPath)) @unlink($uploadedPhotoPath);
    broadcast_json([
        'ok' => false,
        'error' => 'وقتی عکس همراه پیام ارسال می‌شود، متن باید حداکثر ۱۰۲۴ کاراکتر باشد تا عکس، متن و دکمه‌ها در یک پیام واحد ارسال شوند. متن را کوتاه‌تر کن یا عکس را حذف کن.'
    ]);
}

// Get target users
if ($targetType === 'self') {
    $users = [(int)$userId];
} else {
    $sql = "SELECT id FROM users WHERE 1=1";
    if ($targetType === 'active') {
        $sql .= " AND id IN (SELECT DISTINCT user_id FROM orders WHERE status = 'DELIVERED')";
    } elseif ($targetType === 'inactive') {
        $sql .= " AND id NOT IN (SELECT DISTINCT user_id FROM orders WHERE status = 'DELIVERED')";
    }
    $stmt = $pdo->query($sql);
    $users = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN));
}

$totalUsers = count($users);
$sent = 0;
$failed = 0;
$blocked = 0;

try {
    foreach ($users as $uid) {
        $params = ['chat_id' => $uid];
        if ($replyMarkup) $params['reply_markup'] = $replyMarkup;

        if ($photoFileId !== '' || $uploadedPhotoPath) {
            // Single Telegram message path: image + caption text + inline buttons all travel in this sendPhoto call.
            // For local uploads tg() JSON-encodes reply_markup in multipart mode (v27.40 fix).
            $params['photo'] = $photoFileId !== '' ? $photoFileId : $uploadedPhotoPath;
            if ($htmlMessage !== '') {
                $params['caption'] = $htmlMessage;
                $params['parse_mode'] = 'HTML';
            }
            $result = tg('sendPhoto', $params);
            if ($photoFileId === '') {
                $newFileId = broadcast_extract_photo_file_id(is_array($result) ? $result : []);
                if ($newFileId !== '') $photoFileId = $newFileId;
            }
        } else {
            $params['text'] = $htmlMessage;
            $params['parse_mode'] = 'HTML';
            $result = tg('sendMessage', $params);
        }

        broadcast_count_result($result, $sent, $failed, $blocked);
        usleep(45000);
    }
} finally {
    if ($uploadedPhotoPath && file_exists($uploadedPhotoPath)) @unlink($uploadedPhotoPath);
}

try {
    $pdo->prepare("CREATE TABLE IF NOT EXISTS broadcast_logs (id BIGINT AUTO_INCREMENT PRIMARY KEY, admin_id BIGINT NULL, message TEXT NULL, target_type VARCHAR(50) NULL, total_users INT NOT NULL DEFAULT 0, sent_count INT NOT NULL DEFAULT 0, failed_count INT NOT NULL DEFAULT 0, blocked_count INT NOT NULL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci")->execute();
    $stmt = $pdo->prepare("INSERT INTO broadcast_logs (admin_id, message, target_type, total_users, sent_count, failed_count, blocked_count) VALUES (?, ?, ?, ?, ?, ?, ?)");
    $stmt->execute([$userId, broadcast_plain_log_message($rawMessage), $targetType, $totalUsers, $sent, $failed, $blocked]);
} catch (Throwable $e) {
    error_log('broadcast_log_failed: ' . $e->getMessage());
}

gemaccAdminActionNotify('پیام همگانی ارسال شد', ['گروه هدف'=>$targetType, 'کل کاربران'=>$totalUsers, 'ارسال موفق'=>$sent, 'ناموفق'=>$failed, 'مسدود/بلاک'=>$blocked, 'دکمه‌ها'=>implode(', ', $buttonKeys)], $userId);

broadcast_json([
    'ok' => true,
    'message' => 'ارسال شد',
    'stats' => ['total' => $totalUsers, 'sent' => $sent, 'failed' => $failed, 'blocked' => $blocked],
]);
