<?php

namespace App\Services;

use App\Models\ActivityReward;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Writer\PngWriter;
use GuzzleHttp\Client;
use Telegram\Bot\Api;
use App\Models\Config;
use Telegram\Bot\FileUpload\InputFile;
use Telegram\Bot\HttpClients\GuzzleHttpClient;
use Illuminate\Support\Facades\Log;
use App\Jobs\SendTelegramMessageJob;
use App\Jobs\SendTelegramGroupMessageJob;

abstract class BaseService
{
    const YES = 1;
    const NOT = 0;

    public static string $MODEL = "";




    /**
     * @description: 模型
     * @return string
     */
    public static function model(): string
    {
        return static::$MODEL;
    }

    /**
     * @description: 获取查询条件
     * @param array $search
     * @return array
     */
    abstract public static function getWhere(array $search = []): array;

    /**
     * @description: 枚举
     * @return {*}
     */
    public static function enum(): string
    {
        return '';
    }

    /**
     * @description: 生成充值二维码
     * @param {*} $address 充值地址
     * @return {*}
     */
    public static function createRechargeQrCode($address = '')
    {
        $content = $address;
        $qrSize = 300;
        $font = 4;
        $textHeight = 20;
        $padding = 10;

        // 生成二维码图像对象
        $result = Builder::create()
            ->writer(new PngWriter())
            ->data($content)
            ->size($qrSize)
            ->margin(0)
            ->build();

        $qrImage = imagecreatefromstring($result->getString());

        // 创建画布（加上下方文字区和边距）
        $canvasWidth = $qrSize + $padding * 2;
        $canvasHeight = $qrSize + $textHeight + $padding * 2;
        $image = imagecreatetruecolor($canvasWidth, $canvasHeight);

        // 背景白色
        $white = imagecolorallocate($image, 255, 255, 255);
        imagefill($image, 0, 0, $white);

        // 黑色字体
        $black = imagecolorallocate($image, 0, 0, 0);

        // 合并二维码图像
        imagecopy($image, $qrImage, $padding, $padding, 0, 0, $qrSize, $qrSize);

        // 写文字
        $textWidth = imagefontwidth($font) * strlen($content);
        $x = ($canvasWidth - $textWidth) / 2;
        $y = $qrSize + $padding + 5;
        imagestring($image, $font, $x, $y, $content, $black);

        // 生成文件名
        $filename = $address . '.png';
        $relativePath = 'recharge/' . $filename;
        $storagePath = storage_path('app/public/' . $relativePath);

        // 确保目录存在
        @mkdir(dirname($storagePath), 0777, true);

        // 保存图片到文件
        imagepng($image, $storagePath);

        // 清理
        imagedestroy($qrImage);
        imagedestroy($image);

        // 返回 public 存储路径（可用于 URL）
        return 'storage/' . $relativePath; // 或返回 Storage::url($relativePath);
    }

    /**
     * 判断指定地址的二维码是否已生成（已存在文件）
     *
     * @param string $address 充值地址
     * @return
     */
    public static function rechargeQrCodeExists(string $address)
    {
        $filename = $address . '.png';
        $relativePath = 'recharge/' . $filename;
        $storagePath = storage_path('app/public/' . $relativePath);
        $path = '';
        if (file_exists($storagePath)) {
            $path = 'storage/' . $relativePath;
        }
        return $path;
    }


    /**
     * @description: 转成树形数据
     * @param {*} $list 初始数据
     * @param {*} $pid 父id
     * @param {*} $level 层级
     * @param {*} $pid_name pid字段名称 默认pid
     * @param {*} $id_name  主键id 名称
     * @return {*}
     */
    public static function toTree($list, $pid = 0, $level = 0, $pid_name = 'pid', $id_name = 'id')
    {
        $arr = [];
        $level++;
        foreach ($list as $k => $v) {
            if ($pid == $v[$pid_name]) {
                $v['level'] = $level;
                $v['children'] = self::toTree($list, $v[$id_name], $level, $pid_name, $id_name);
                $arr[] = $v;
            }
        }
        return $arr;
    }


    public static function buildTree($list,$pid_name='pid',$id_name='id')
    {
        // 创建映射表
        $map = [];
        foreach ($list as $item) {
            $map[$item[$id_name]] = $item;
        }

        // 找到顶级节点
        $topLevelNodes = [];
        foreach ($map as $key => $value) {
            if (!isset($map[$value[$pid_name]])) {
                $topLevelNodes[$key] = &$map[$key];
            }
        }

        // 构建树形结构
        $tree = [];
        foreach ($map as &$item) {
            if (isset($item[$pid_name]) && isset($map[$item[$pid_name]])) {
                $parent = &$map[$item[$pid_name]];
                if (!isset($parent['children'])) {
                    $parent['children'] = [];
                }
                $parent['children'][] = &$item;
            } else {
                $tree[] = &$item;
            }
        }

        return $tree;
    }

    /**
     * @description: 实例化TG
     * @return {*}
     */
    public static function telegram()
    {
        return app(Api::class);
    }

    protected static function telegramForAttempt(int $attempt): Api
    {
        $transport = self::getTelegramTransportForAttempt($attempt);
        $clientOptions = [];

        if (!empty($transport['proxy_url'])) {
            $clientOptions['proxy'] = $transport['proxy_url'];
        }

        $telegram = new Api(config('services.telegram.token'), false, new GuzzleHttpClient(new Client($clientOptions)));
        $telegram->setTimeOut((int)$transport['timeout']);
        $telegram->setConnectTimeOut((int)$transport['connect_timeout']);

        return $telegram;
    }

    protected static function getTelegramTransportForAttempt(int $attempt): array
    {
        if ($attempt <= 1 || !config('services.telegram.proxy.enabled', true)) {
            return [
                'name' => 'direct',
                'proxy_url' => '',
                'timeout' => (int)config('services.telegram.first_timeout', 5),
                'connect_timeout' => (int)config('services.telegram.first_connect_timeout', 3),
            ];
        }

        return [
            'name' => 'squid',
            'proxy_url' => self::getTelegramProxyUrl(),
            'timeout' => (int)config('services.telegram.proxy_timeout', 20),
            'connect_timeout' => (int)config('services.telegram.proxy_connect_timeout', 8),
        ];
    }

    protected static function getTelegramProxyUrl(): string
    {
        $scheme = config('services.telegram.proxy.scheme', 'http');
        $host = config('services.telegram.proxy.host', '');
        $port = config('services.telegram.proxy.port', '3128');
        $username = (string)config('services.telegram.proxy.username', '');
        $password = (string)config('services.telegram.proxy.password', '');

        if (empty($host)) {
            return '';
        }

        if ($username === '' && $password === '') {
            return "{$scheme}://{$host}:{$port}";
        }

        if ($password === '') {
            return "{$scheme}://" . rawurlencode($username) . "@{$host}:{$port}";
        }

        return "{$scheme}://" . rawurlencode($username) . ':' . rawurlencode($password) . "@{$host}:{$port}";
    }

    protected static function sendTelegramRequest(callable $callback, string $action, array $context = [])
    {
        $maxAttempts = max(1, (int)config('services.telegram.retry_attempts', 2));
        $delaySeconds = max(1, (int)config('services.telegram.retry_delay_seconds', 3));

        for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
            $transport = self::getTelegramTransportForAttempt($attempt);
            try {
                return $callback(self::telegramForAttempt($attempt));
            } catch (\Throwable $exception) {
                $safeMessage = self::sanitizeTelegramError($exception->getMessage());
                $logContext = array_merge($context, [
                    'action' => $action,
                    'attempt' => $attempt,
                    'max_attempts' => $maxAttempts,
                    'transport' => $transport['name'],
                    'using_proxy' => !empty($transport['proxy_url']),
                    'timeout' => $transport['timeout'],
                    'connect_timeout' => $transport['connect_timeout'],
                    'error' => $safeMessage,
                    'exception' => get_class($exception),
                ]);

                if ($attempt >= $maxAttempts || !self::isRetryableTelegramError($safeMessage)) {
                    Log::channel('issue')->warning('Telegram请求失败', $logContext);
                    throw new \RuntimeException($safeMessage);
                }

                $retryAfter = self::getTelegramRetryAfter($safeMessage, $attempt, $delaySeconds);
                Log::channel('issue')->warning('Telegram请求失败，准备重试', $logContext + [
                    'retry_after_seconds' => $retryAfter,
                ]);
                sleep($retryAfter);
            }
        }
    }

    protected static function sanitizeTelegramError(string $message): string
    {
        return preg_replace('/bot\d+:[A-Za-z0-9_-]+/', 'bot[redacted]', $message);
    }

    protected static function isRetryableTelegramError(string $message): bool
    {
        return str_contains($message, 'Too Many Requests')
            || str_contains($message, 'retry after')
            || str_contains($message, 'cURL error 7')
            || str_contains($message, 'cURL error 28')
            || str_contains($message, 'Failed to connect')
            || str_contains($message, 'Connection timed out')
            || str_contains($message, 'Operation timed out');
    }

    protected static function getTelegramRetryAfter(string $message, int $attempt, int $delaySeconds): int
    {
        $maxDelaySeconds = max(1, (int)config('services.telegram.retry_max_delay_seconds', 10));

        if (preg_match('/retry after (\d+)/i', $message, $matches)) {
            return min($maxDelaySeconds, (int)$matches[1] + 1);
        }

        if (self::isTelegramConnectionError($message)) {
            return 0;
        }

        return min($maxDelaySeconds, $delaySeconds * $attempt);
    }

    protected static function isTelegramConnectionError(string $message): bool
    {
        return str_contains($message, 'cURL error 7')
            || str_contains($message, 'cURL error 28')
            || str_contains($message, 'Failed to connect')
            || str_contains($message, 'Connection timed out')
            || str_contains($message, 'Operation timed out');
    }

    // /**
    //  * @description: 群组通知（自动分段发送，支持中文与多字节字符）
    //  * @param string $text 通知内容
    //  * @param array $keyboard 操作按钮
    //  * @param string $image 图片路径（可选）
    //  * @param bool $isTop 是否置顶第一条消息
    //  */
//     public static function bettingGroupNotice($text, $keyboard = [], $image = '', $isTop = false)
//     {
//         $bettingGroup = Config::where('field', 'betting_group')->first()->val;
//         $telegram = self::telegram();
//
//         $maxLen = 1024; // Telegram 限制：最多 1024 个字符
//         $textParts = [];
//         $textLength = mb_strlen($text, 'UTF-8');
//         for ($i = 0; $i < $textLength; $i += $maxLen) {
//             $textParts[] = mb_substr($text, $i, $maxLen, 'UTF-8');
//         }
//
//         $firstMessageId = null;
//
//         foreach ($textParts as $index => $partText) {
//             $botMsg = [
//                 'chat_id' => "@{$bettingGroup}",
//                 'text' => $partText,
//             ];
//
//             if (count($keyboard) > 0 && $index === 0) {
//                 $botMsg['reply_markup'] = json_encode(['inline_keyboard' => $keyboard]);
//             }
//
//             if (!empty($image) && $index === 0) {
//                 // 第一条带图片
//                 $botMsg['photo'] = InputFile::create($image);
//                 $botMsg['caption'] = $partText;
//                 $botMsg['protect_content'] = true;
//                 $response = $telegram->sendPhoto($botMsg);
//             } else {
//                 $response = $telegram->sendMessage($botMsg);
//             }
//
//             if ($isTop && $index === 0 && $response && $response->get('message_id')) {
//                 $firstMessageId = $response->get('message_id');
//             }
//
//             // 防止限流（可选）
//             usleep(300000);
//         }
//
//         if ($isTop && $firstMessageId) {
//             $telegram->pinChatMessage([
//                 'chat_id' => "@{$bettingGroup}",
//                 'message_id' => $firstMessageId
//             ]);
//         }
//     }


    /**
     * @description: 群组通知
     * @apiParam string $text 通知内容
     * @apiParam array $keyboard 操作按钮
     * @apiParam string $separator 分隔符
     * @apiParam boolean $isTop 是否置顶
     */
    public static function bettingGroupNotice($text, $keyboard = [], $image = '', $isTop = false, $separator = "\n"): array
    {
        $bettingGroup = Config::where('field', 'betting_group')->first()->val;
        if (empty($separator)) $separator = "\n";
        $array = explode($separator, $text);

        $res = [];
        // 为空只发图片
        if (empty($text) && !empty($image)) {
            $botMsg = [
                'chat_id' => "@{$bettingGroup}",
            ];
            $botMsg['photo'] = InputFile::create($image);
            $botMsg['caption'] = $text;
            $botMsg['protect_content'] = true;  // 防止转发
            if (count($keyboard) > 0) {
                $botMsg['reply_markup'] = json_encode(['inline_keyboard' => $keyboard]);
            }
            $response = self::sendTelegramRequest(fn(Api $telegram) => $telegram->sendPhoto($botMsg), 'sendPhoto', [
                'chat_id' => $botMsg['chat_id'],
                'has_image' => true,
            ]);
        } else {
            foreach ($array as $key => $line) {
                if (empty(str_ireplace(" ", "", str_ireplace("\n", '', $line)))) {
                    unset($array[$key]);
                } else {
                    $array[$key] .= $separator;
                }
            }
            $texts = [];
            $len = !empty($image) ? 1024 : 4096;
            foreach ($array as $item) {
                if (count($texts) > 1) $len = 4096;
                if (count($texts) == 0 || strlen($texts[count($texts) - 1] . $item) > $len) {
                    $texts[] = $item;
                } else {
                    $texts[count($texts) - 1] .= $item;
                }
            }


            foreach ($texts as $index => $item) {
                $botMsg = [
                    'chat_id' => "@{$bettingGroup}",
                    'text' => $item,
                ];
                if ($index > 0) {
                    $res[] = $botMsg;
                    self::sendTelegramRequest(fn(Api $telegram) => $telegram->sendMessage($botMsg), 'sendMessage', [
                        'chat_id' => $botMsg['chat_id'],
                        'chunk_index' => $index,
                        'has_image' => false,
                    ]);
                } else {
                    if (count($keyboard) > 0) {
                        $botMsg['reply_markup'] = json_encode(['inline_keyboard' => $keyboard]);
                    }
                    if (!empty($image)) {
                        unset($botMsg['text']);
                        $botMsg['photo'] = InputFile::create($image);
                        $botMsg['caption'] = $item;
                        $botMsg['protect_content'] = true;

                        $res[] = $botMsg;
                        $response = self::sendTelegramRequest(fn(Api $telegram) => $telegram->sendPhoto($botMsg), 'sendPhoto', [
                            'chat_id' => $botMsg['chat_id'],
                            'chunk_index' => $index,
                            'has_image' => true,
                        ]);
                    } else {
                        $res[] = $botMsg;
                        $response = self::sendTelegramRequest(fn(Api $telegram) => $telegram->sendMessage($botMsg), 'sendMessage', [
                            'chat_id' => $botMsg['chat_id'],
                            'chunk_index' => $index,
                            'has_image' => false,
                        ]);
                    }
                    if ($isTop === true) {
                        self::sendTelegramRequest(fn(Api $telegram) => $telegram->pinChatMessage([
                            'chat_id' => "@{$bettingGroup}",
                            'message_id' => $response->get('message_id')
                        ]), 'pinChatMessage', [
                            'chat_id' => "@{$bettingGroup}",
                            'message_id' => $response->get('message_id'),
                        ]);
                    }
                }


            }
        }


        return $res;
    }

    /**
     * @description: 异步群组通知
     * @param {string} $text 通知内容
     * @param {array} $keyboard 操作按钮
     * @param {*string} $image  图片
     * @return {*}
     */
    public static function asyncBettingGroupNotice($text, $keyboard = [], $image = '', $isTop = false): void
    {
        SendTelegramGroupMessageJob::dispatch($text, $keyboard, $image, $isTop);
    }

    /**
     * @description: 发送消息
     * @param {string} $chatId 聊天ID
     * @param {string} $text 消息内容
     * @param {array} $keyboard 操作按钮
     * @param {*string} $image  图片
     * @return {*}
     */
    public static function sendMessage($chatId, $text, $keyboard = [], $image = ''): void
    {
        $botMsg = [
            'chat_id' => $chatId,
        ];
        if (count($keyboard) > 0) {
            $botMsg['reply_markup'] = json_encode(['inline_keyboard' => $keyboard]);
        }
        if ($image != '') {
            $botMsg['photo'] = InputFile::create($image);
            $botMsg['caption'] = $text;
            $botMsg['protect_content'] = false;  // 防止转发
            self::sendTelegramRequest(fn(Api $telegram) => $telegram->sendPhoto($botMsg), 'sendPhoto', [
                'chat_id' => $chatId,
                'has_image' => true,
            ]);
        } else {
            $botMsg['text'] = $text;
            self::sendTelegramRequest(fn(Api $telegram) => $telegram->sendMessage($botMsg), 'sendMessage', [
                'chat_id' => $chatId,
                'has_image' => false,
            ]);
        }
    }

    /**
     * @description: 异步发送消息
     * @param {string} $chatId 聊天ID
     * @param {string} $text 消息内容
     * @param {array} $keyboard 操作按钮
     * @param {*string} $image  图片
     * @return {*}
     */
    public static function asyncSendMessage($chatId, $text, $keyboard = [], $image = ''): void
    {
        SendTelegramMessageJob::dispatch($chatId, $text, $keyboard, $image);
    }

    /**
     * @description: 弹窗提示
     * @param {*} $memberId
     * @param {*} $address
     * @return {*}
     */
    public static function alertNotice($callbackId, $text): void
    {
        self::telegram()->answerCallbackQuery([
            'callback_query_id' => $callbackId,
            'text' => $text,
            'show_alert' => true // 显示为弹窗
        ]);
    }

    public static function log($message, $context = [])
    {
        Log::error($message, $context);
    }

    /**
     * @description: 获取操作按钮
     * @return {*}
     */
    public static function getOperateButton()
    {

        $replyInfo = KeyboardService::findOne(['button' => '投注菜单']);

        if ($replyInfo && $replyInfo->buttons) {
            $buttons = json_decode($replyInfo->buttons, true);

            foreach ($buttons as $row) {
                $inlineButton[] = [];
                foreach ($row as $button) {
                    $btn = ['text' => $button['text']];
                    if (strpos($button['url'], 'http') === 0) {
                        $btn['url'] = $button['url'];
                    } else {
                        $btn['callback_data'] = $button['url'];
                    }
                    $inlineButton[count($inlineButton) - 1][] = $btn;
                }
            }
            $inlineButton = array_values($inlineButton);
            return $inlineButton;
        }
        $inlineButton = [];
        return $inlineButton;
    }

    // 获取字符串最后几个字符
    public static function getLastChar($str, $num = 1)
    {
        $length = mb_strlen($str, 'UTF-8');
        $lastChar = mb_substr($str, $length - 1, $num, 'UTF-8');
        return $lastChar;
    }

    public static function generateRandomString($length = 8)
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $randomString = '';

        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, strlen($characters) - 1)];
        }

        return $randomString;
    }

    public static function generateRandomNumber($length = 8)
    {
        $characters = '0123456789';
        $randomString = '';

        for ($i = 0; $i < $length; $i++) {
            $randomString .= rand(1, 9);
        }

        return $randomString;
    }

    public static function hideMiddleDigits($number, $hideCount = 4)
    {
        $length = strlen($number);

        if ($length <= $hideCount) {
            // 数字太短，全部隐藏
            return str_repeat("*", $length);
        }

        // 计算中间开始隐藏的位置
        $startLen = floor(($length - $hideCount) / 2);
        $endLen = $length - $hideCount - $startLen;

        $start = substr($number, 0, $startLen);
        $end = substr($number, -$endLen);

        return $start . str_repeat("*", $hideCount) . $end;
    }

    // 生成订单号
    public static function createOrderNo($prefix = 'pc28_', $memberId = null)
    {
        // 处理会员ID，获取后四位
        if ($memberId) {
            $memberSuffix = str_pad(substr($memberId, -4), 4, '0', STR_PAD_LEFT);
        } else {
            $memberSuffix = '0000'; // 默认值
        }

        // 时间部分
        $timePart = date('YmdHis');

        // 随机部分增加唯一性
        $randomPart = mt_rand(1000, 9999);

        return $prefix . $timePart . $randomPart . $memberSuffix;
    }


    /**
     * @description: 生成支付二维码
     * @param {*} $address 支付地址
     * @return {*}
     */
    public static function createPaymentQrCode($address = '')
    {
        // $content = $address;
        $content = '';
        $qrSize = 300;
        $font = 4;
        $textHeight = 20;
        $padding = 10;

        // 生成二维码图像对象
        $result = Builder::create()
            ->writer(new PngWriter())
            ->data($address)
            ->size($qrSize)
            ->margin(0)
            ->build();

        $qrImage = imagecreatefromstring($result->getString());

        // 创建画布（加上下方文字区和边距）
        $canvasWidth = $qrSize + $padding * 2;
        $canvasHeight = $qrSize + $textHeight + $padding * 2;
        $image = imagecreatetruecolor($canvasWidth, $canvasHeight);

        // 背景白色
        $white = imagecolorallocate($image, 255, 255, 255);
        imagefill($image, 0, 0, $white);

        // 黑色字体
        $black = imagecolorallocate($image, 0, 0, 0);

        // 合并二维码图像
        imagecopy($image, $qrImage, $padding, $padding, 0, 0, $qrSize, $qrSize);

        // 写文字
        $textWidth = imagefontwidth($font) * strlen($content);
        $x = ($canvasWidth - $textWidth) / 2;
        $y = $qrSize + $padding + 5;
        imagestring($image, $font, $x, $y, $content, $black);

        $address_name = self::generateRandomString(20) . time();

        // 生成文件名
        $filename = $address_name . '.png';
        $relativePath = 'payment/' . $filename;
        $storagePath = storage_path('app/public/' . $relativePath);

        // 确保目录存在
        @mkdir(dirname($storagePath), 0777, true);

        // 保存图片到文件
        imagepng($image, $storagePath);

        // 清理
        imagedestroy($qrImage);
        imagedestroy($image);

        // 返回 public 存储路径（可用于 URL）
        return 'storage/' . $relativePath; // 或返回 Storage::url($relativePath);
    }

}