<?php

error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED);
date_default_timezone_set('Asia/Tehran');
const ERROR_LOGS_FILE_DIR = __DIR__ . "/../Storage/logs/broadcast.log";
const BATCH_SIZE = 200;
const GROUP_SEND_BATCH_SIZE = 20;

ini_set("log_errors", 1);
ini_set("error_log", ERROR_LOGS_FILE_DIR);

require __DIR__ . '/../../vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../../');
$dotenv->load();

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Promise\Utils;
use Medoo\Medoo;

define('API_KEY', $_ENV['API_KEY']);
define("LOG_CHAT", $_ENV['ADMIN']);

/**
 * @param $results
 * @param $successCount
 * @param $failCount
 * @return void
 */
function processSettleRequest($results, &$successCount, &$failCount): void
{
    foreach ($results as $result) {
        if ($result['state'] == 'fulfilled') {
            if ($result['value']['ok']) {
                $successCount++;
            } else {
                $failCount++;
            }
        } else {
            $failCount++;
        }
    }
}

/**
 * @param $httpClient
 * @param $task
 * @param $user
 * @param $promises
 * @param $successCount
 * @param $failCount
 * @return void
 */
function processBroadcast($httpClient, $task, $user, &$promises, &$successCount, &$failCount): void
{
    if (count($promises) >= GROUP_SEND_BATCH_SIZE) {
        // وقتی تعداد درخواست‌ها بیشتر از حد مجاز شد، منتظر اتمام آن‌ها می‌شویم
        $results = Utils::settle($promises)->wait();
        $promises = [];
        processSettleRequest($results, $successCount, $failCount);
        sleep(1);
    }

    $user_id = $user['user_id'];
    // اضافه کردن درخواست‌ها به لیست Promise ها
    $promises[] = $httpClient->postAsync("https://api.telegram.org/bot" . API_KEY . "/" . (($task['type'] === 'forward') ? 'forwardMessage' : 'copyMessage'), [
        'form_params' => [
            'chat_id' => $user_id,
            'from_chat_id' => $task['from_chat_id'],
            'message_id' => $task['message_id']
        ],
    ])->then(
        function ($response) use ($user_id) {
            $body = $response->getBody()->getContents();
            $res = json_decode($body, true);
            $res['user_id'] = $user_id;
            return $res;
        },
        function (RequestException $e) use ($user_id, &$bot_blocked) {
            if ($e->hasResponse()) {
                return json_decode($e->getResponse()->getBody()->getContents(), true);
            }
            else return ['ok' => false, 'description' => $e->getMessage()];
        }
    );
}

/**
 * @param array $task
 * @param Client $httpClient
 * @param Medoo $database
 * @return void
 */
function processTask(array $task, Client $httpClient, Medoo $database): void
{
    echo $task['id'] . " Sending started" . PHP_EOL;
    $successCount = 0;
    $failCount = 0;
    $promises = [];
    $start_sending = time();

    $countAllUsers = $database->count("users_tbl", ['block' => 0]);

    $database->select('users_tbl', ['user_id'], ['block' => 0, 'LIMIT' => [$task['start_number'], BATCH_SIZE]], function ($user) use ($database, $httpClient, $task, &$promises, &$successCount, &$failCount) {
        processBroadcast($httpClient, $task, $user, $promises, $successCount, $failCount);
    });

    // وقتی درخواست‌ها تمام شدند، آن‌ها را پردازش می‌کنیم
    if (count($promises) > 0) {
        $results = Utils::settle($promises)->wait();
        processSettleRequest($results, $successCount, $failCount);
    }

    // بروزرسانی وضعیت وظیفه در دیتابیس
    $new_status = ($countAllUsers <= $task['start_number'] + BATCH_SIZE) ? 'done' : 'sending';
    $end_time = time();
    $database->update('send_tbl', [
        'status' => $new_status,
        'sent[+]' => $successCount,
        'failed[+]' => $failCount,
        'end_time' => ($new_status == 'done') ? $end_time : null,
        'start_number[+]' => BATCH_SIZE
    ], ['id' => $task['id']]);

    if($new_status == 'done') {
        $members = number_format($successCount + $failCount);
        $successCount = number_format($successCount);
        $failCount = number_format($failCount);
        $remaining_time = $end_time - $start_sending;
        if ($remaining_time > 0) $remaining_time = convertSecondsToString($remaining_time);
        else $remaining_time = "در کسری از ثانیه";
        $httpClient->postAsync("https://api.telegram.org/bot" . API_KEY . "/sendMessage", [
            'form_params' => [
                'chat_id' => $task['admin'],
                'text' => "✔️ ارسال همگانی با شناسه <code>{$task['id']}</code> به اتمام رسید.

✅ ارسال موفق: $successCount
❌ ارسال ناموفق: $failCount
👥 کل ارسال: $members
⏳ مدت ارسال: $remaining_time",
                'parse_mode' => 'Html'
            ],
        ])->wait();
        echo $task['id'] . " sending completed: Success = $successCount, Fail = $failCount" . PHP_EOL;
    }
}


while (true) {
    try {
        $try_time = time();
        echo "Script started ..." . PHP_EOL;
        $database = db_connect();
        $httpClient = new Client();

        $pendingTask = $database->get("send_tbl", '*', [
            'status' => ['pending', 'sending'],
            'ORDER' => ['start_time' => 'ASC'],
            'LIMIT' => 1
        ]);

        if($pendingTask) processTask($pendingTask, $httpClient, $database);

    } catch (Throwable $e) {
        echo "Error in loop: " . $e->getMessage() . PHP_EOL;
        error_log("Error occurred in broadcasting: " . $e->getMessage());
    }

    $end_try_time = time();
    $sleep_time = 60 - ($end_try_time - $try_time);
    sleep($sleep_time);
}
