<?php

namespace App\Services;

use App\Jobs\SendTelegramGroupMessageJob;
use App\Models\Config;
use App\Models\Message;
use App\Models\PcCao;
use App\Models\PcCaoHistory;
use App\Models\PcIssue;
use App\Models\PcPrediction;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class PcIssueService extends BaseService
{
    public static string $MODEL = PcIssue::class;

    const COUN_TDOWN = 75; //每轮开盘的时长（秒数）
    const FENG_PAN = 15; //提前15秒封盘

    public static function index(): void
    {
        //封盘倒数  提前20秒提醒
        PcIssueService::syncCountdownIssue();
        //对该封盘的进行封盘
        PcIssueService:: fengPan();
        //将未开奖的进行开奖
        PcIssueService:: kaiJiang();
        //创建新的期号
        PcIssueService::createIssueNo();


    }

    //封盘倒数
    public static function syncCountdownIssue()
    {
        $info = PcIssue::where('status', PcIssue::STATUS_BETTING)->orderBy('end_time')->first();
        if ($info) {
            $now_date = date('Y-m-d H:i:s', time() + 60); // 提前60秒
            if ($info['end_time'] < $now_date) {
                $maintenanceSwitch = Config::where('field', 'maintenance_switch')->first()->val;
                $replyInfo = KeyboardService::findOne(['button' => '封盘倒数']);
                if ($replyInfo) {
                    $text = $replyInfo->reply;
                    $buttons = json_decode($replyInfo->buttons, true);
                    $image = $replyInfo->image;
                    if ($image) {
                        $image = url($image);
                    }
                    if (Cache::has('issue_countdown_' . $info->issue_no)) {

                    } else {
                        $pc28Switch = Config::where('field', 'pc28_switch')->first()->val;
                        if ($maintenanceSwitch != 1 && $pc28Switch == 1) {
                            self::asyncBettingGroupNotice($text, $buttons, $image);
                            Cache::put('issue_countdown_' . $info->issue_no, true, 60); // 缓存50秒，防止多次发送
                        }
                    }
                }
            }
        }

    }

    private static function fengPan()
    {
        $list = PcIssue::where('status', PcIssue::STATUS_BETTING)
            ->orderByDesc('id')->get();
        $now = time();
        $pc28Switch = Config::where('field', 'pc28_switch')->first()->val;
        $now += self::FENG_PAN; //提前封盘时间

        foreach ($list as $index => $item) {
            if (strtotime($item->end_time) < $now) {
                $item->status = PcIssue::STATUS_CLOSE;
                $item->save();

                if ($index == 0 && $pc28Switch == 1) {
                    $replyInfo = KeyboardService::findOne(['button' => '停止下注']);
                    if ($replyInfo) {
                        $text = $replyInfo->reply;
                        $buttons = json_decode($replyInfo->buttons, true);
                        $image = $replyInfo->image;
                        if ($image) $image = url($image);
                        //停止下注的信息不发了
//                        self::asyncBettingGroupNotice($text, $buttons, $image);
                    }

                    // 统计投注情况通知   xxxx期投注统计
                    BetService::statNotice($item->issue_no);

                    $replyInfo = KeyboardService::findOne(['button' => '封盘开奖']);
                    if ($replyInfo) {
                        $text = $replyInfo->reply;
                        $buttons = json_decode($replyInfo->buttons, true);
                        $image = $replyInfo->image;
                        if ($image) $image = url($image);
                        self::asyncBettingGroupNotice($text, $buttons, $image);
                    }
                }


            }
        }
    }

    private static function kaiJiang(): void
    {
        $list = PcIssue::where('status', PcIssue::STATUS_CLOSE)
            ->orderByDesc('id')->get();
        $now = time();
        foreach ($list as $index => $item) {
            if (strtotime($item->end_time) <= $now) {
                DB::beginTransaction();
                try {
                    if (!empty($item->advance_keno)) {
                        $keno = json_decode($item->advance_keno, true);
                    } else {
                        $keno = static::getKeno();
                    }
                    $item->keno = json_encode($keno);
                    $winningNumbers = static::getWinningNumbers($keno);
                    $item->winning_numbers = implode(',', $winningNumbers);
                    $item->save();


                    $winArr = array_map('intval', $winningNumbers);
                    // 组合
                    $sum = array_sum($winArr);
                    $combo = [];
                    $sumSize = IssueService::calculateSumSize($sum);  // 总和大小
                    $combo[] = $sumSize;

                    $sumOddEven = IssueService::calculateOddEven($sum); // 总和单双
                    $combo[] = $sumOddEven;
                    $sumExtremeSize = IssueService::calculateSumExtremeSize($sum);  // 总和极值
                    if ($sumExtremeSize) {
                        $combo[] = $sumExtremeSize;
                    }
                    $sumBaoZi = IssueService::isBaoZi($winArr[0], $winArr[1], $winArr[2]); // 豹子
                    if ($sumBaoZi) {
                        $combo[] = $sumBaoZi;
                    }
                    $sumPair = IssueService::isPair($winArr[0], $winArr[1], $winArr[2]); // 对子
                    if ($sumPair) {
                        $combo[] = $sumPair;
                    }
                    $sumStraight = IssueService::isStraight($winArr[0], $winArr[1], $winArr[2]); // 顺子
                    if ($sumStraight) {
                        $combo[] = $sumStraight;
                    }
                    $tail = IssueService::getLastDigit($sum); // 总和尾数
                    if ($tail != 0 && $tail != 9) {
                        $combo[] = '尾' . $tail; // 尾数
                    }


                    $key = 'lottery_numbers_' . $item->issue_no;
                    $combo = implode(' ', $combo);

                    if (Cache::add($key, $item->winning_numbers, 100)) {
                        self::lotteryDraw($item->issue_no, $item->winning_numbers, $combo, '', $index);
                    }

                    $pc28Switch = Config::where('field', 'pc28_switch')->first()->val;
                    //更新游戏开关的切换
                    if ($pc28Switch == 1) Config::setPc28Switch();

                    $awards = IssueService::award(explode(',', $item->winning_numbers));
                    //预测结果
                    PcPrediction::result($item->issue_no, $item->winning_numbers, $awards);
                    //自开奖以来的结果统计
                    PcCao::updateData($awards);
                    //每天开奖结果统计
                    PcCaoHistory::updateData($awards);
                    DB::commit();
                } catch (\Exception $e) {
                    Db::rollBack();
                    Log::error("开奖失败：{$item->issue_no}期 on line 179 of PcIssueService.php");
                    Log::error("原因：");
                    Log::error($e->getMessage());
                }
            }
        }
    }

    //开奖
    private static function lotteryDraw($issue_no, $winning_numbers, $combo, $recordImage, $index)
    {
        $info = PcIssue::where('issue_no', $issue_no)->first();
        if (!$info) {
            return ['code' => self::NOT, 'msg' => '期号不存在'];
        }
        if ($info->status == PcIssue::STATUS_DRAW) {
            return ['code' => self::NOT, 'msg' => '期号状态不正确'];
        }

        $winArr = array_map('intval', explode(',', $winning_numbers));
        // 计算中奖
        $awards = IssueService::award(explode(',', $winning_numbers));
        DB::beginTransaction();
        try {
            $info->status = PcIssue::STATUS_DRAW;
            $info->combo = $combo;
            $info->image = $recordImage;
            $info->save();
            $pc28Switch = Config::where('field', 'pc28_switch')->first()->val;
            $replyInfo = KeyboardService::findOne(['button' => '本期开奖']);

            if ($replyInfo) {
                $text = $replyInfo->reply;
                $text .= "\n";
                $text .= $info->issue_no . "： " . implode('+', explode(',', $winning_numbers)) . "=" . array_sum($winArr) . " " . $combo;

                $buttons = json_decode($replyInfo->buttons, true);
                $image = $replyInfo->image;
                if ($image) {
                    $image = url($image);
                }
                if (empty($buttons)) {
                    $serviceAccount = Config::where('field', 'service_account')->first()->val;
                    $buttons[] = [['text' => lang('✅唯一财务'), 'callback_data' => "", 'url' => "https://t.me/{$serviceAccount}"]];

                }
                if ($pc28Switch == 1) SendTelegramGroupMessageJob::dispatch($text, $buttons, $image, true);
            }

            //开发环境不生成开奖图片
            if (env('APP_ENV') != 'local') {
                $recordImage = self::lotteryImage($info->issue_no);
                if ($recordImage) {
                    if ($pc28Switch == 1) SendTelegramGroupMessageJob::dispatch('', [], url($recordImage), false);
                }
            }
            
            //中奖结算 并发送群通知 xxxx期开奖结果
            BetService::betSettled($info->issue_no, $awards);
            DB::commit();
            return ['code' => self::YES, 'msg' => '开奖成功'];
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('开奖失败: ' . $e->getMessage() . $winning_numbers);
            Log::error($e->getLine());
            return ['code' => self::NOT, 'msg' => '开奖失败'];
        }
    }

    // 生成开奖图片
    private static function lotteryImage($issue_no)
    {
        $list = PcIssue::where('issue_no', '<=', $issue_no)->where('status', PcIssue::STATUS_DRAW)->orderBy('issue_no', 'desc')->take(20)->get();
        $records = $list->toArray();

        foreach ($records as $k => $v) {
            $winning_numbers = explode(',', $v['winning_numbers']);
            $v['winning_numbers'] = $winning_numbers;

            // 组合
            $sum = array_sum($winning_numbers);

            $v['sum'] = $sum;
            $sumOddEven = IssueService::calculateOddEven($sum); // 总和单双

            $sumSize = IssueService::calculateSumSize($sum);  // 总和大小
            $v['combo'] = $sumSize . ' ' . $sumOddEven;

            $sumExtremeSize = IssueService::calculateSumExtremeSize($sum);  // 总和极值
            if (!$sumExtremeSize) {
                $sumExtremeSize = '-';
            }
            $v['extreme'] = $sumExtremeSize;

            $tail = IssueService::getLastDigit($sum); // 总和尾数
            if ($tail === 0 || $tail === 9) {
                $tailStr = '-';
            } else {
                $tailStr = '尾' . $tail;
            }
            $v['tail'] = $tailStr;

            $records[$k] = $v;
        }
        $service = new LotteryImageService();
        $url = $service->generate($records);

        PcIssue::where('issue_no', $issue_no)->update(['image' => $url]);
        return $url;
    }

    //创建新的一期
    private static function createIssueNo()
    {
        $issue = PcIssue::orderByDesc('id')->first();
        $now = time();
        if (strtotime($issue->end_time) <= $now) {
            $issue_no = $issue->issue_no;
            $number = preg_replace('/\D/', '', $issue_no);
            $number = (int)$number + 1;
            $new_str = "P" . $number;
            $end_time = strtotime($issue->end_time) + self::COUN_TDOWN; //每轮开盘的时长（秒数）
            $status = $end_time - self::FENG_PAN > $now ? PcIssue::STATUS_BETTING : PcIssue::STATUS_CLOSE;
            $issue1 = PcIssue::create([
                'issue_no' => $new_str,
                'status' => $status,
                'start_time' => $issue->end_time,
                'end_time' => date('Y-m-d H:i:s', $end_time),
            ]);
            //预测
            PcPrediction::prediction($new_str);
            if (strtotime($issue1->end_time) <= $now) {
                static::createIssueNo();
            } else {
                // 群通知，发送玩法规则和开始下注的通知
                static::betting($new_str);
            }
        }
    }

    //群通知，发送玩法规则和开始下注的通知
    private static function betting($issue_no): void
    {
        $info = PcIssue::where('issue_no', $issue_no)->first();
        if (!$info) {
            return;
        }

        if (!in_array($info->status, [PcIssue::STATUS_DRAFT, PcIssue::STATUS_BETTING])) {
            return;
        }
        $info->status = PcIssue::STATUS_BETTING;
        $info->save();

        $maintenanceSwitch = Config::where('field', 'maintenance_switch')->first()->val;
        $pc28Switch = Config::where('field', 'pc28_switch')->first()->val;
        $wanFaGuiZeTime = Cache::get("玩法规则推送时间", 0);
        $now = time();
        if ($now - $wanFaGuiZeTime > (60 * 15)) {
            $replyInfo = KeyboardService::findOne(['button' => '玩法规则']);
            if ($replyInfo) {
                $text = $replyInfo->reply;
                $buttons = json_decode($replyInfo->buttons, true);
                $image = $replyInfo->image;
                if ($image) {
                    $image = url($image);
                }
                if (empty($buttons)) {
                    $buttons = self::getOperateButton();
                }
                Cache::put('玩法规则推送时间', time());
                if ($maintenanceSwitch != 1 && $pc28Switch == 1) self::asyncBettingGroupNotice($text, $buttons, $image);
            }
        }
        $replyInfo = KeyboardService::findOne(['button' => '开始下注']);
        if ($replyInfo) {
            $text = $replyInfo->reply;
            $buttons = json_decode($replyInfo->buttons, true);
            $image = $replyInfo->image;
            if ($image) {
                $image = url($image);
            }
            if ($maintenanceSwitch != 1 && $pc28Switch == 1) self::asyncBettingGroupNotice($text, $buttons, $image);
        }

    }

    //获取随机的20个数字
    private static function getKeno(): array
    {
        $numbers = range(1, 80);
        shuffle($numbers);
        $random_numbers = array_slice($numbers, 0, 20);
        sort($random_numbers);
        return $random_numbers;
    }

    //根据20个数字计算开奖号码
    public static function getWinningNumbers($keno): array
    {
        $winningNumbers = [];
        $sum = $keno[1] + $keno[4] + $keno[7] + $keno[10] + $keno[13] + $keno[16];
        $winningNumbers[0] = $sum % 10;
        $sum = $keno[2] + $keno[5] + $keno[8] + $keno[11] + $keno[14] + $keno[17];
        $winningNumbers[1] = $sum % 10;
        $sum = $keno[3] + $keno[6] + $keno[9] + $keno[12] + $keno[15] + $keno[18];
        $winningNumbers[2] = $sum % 10;
        return $winningNumbers;
    }

    //根据指定0-27的数字 得到20个数字
    public static function getMatchingNumbers(int $target): array|null
    {
        $numbers = range(1, 80);
        $bestMatch = null;
        for ($i = 0; $i < 100000; $i++) {
            shuffle($numbers);
            $selectedNumbers = array_slice($numbers, 0, 20);
            sort($selectedNumbers);
            $firstSum = $selectedNumbers[1] + $selectedNumbers[4] + $selectedNumbers[7] + $selectedNumbers[10] + $selectedNumbers[13] + $selectedNumbers[16];
            $firstDigit = $firstSum % 10;
            $secondSum = $selectedNumbers[2] + $selectedNumbers[5] + $selectedNumbers[8] + $selectedNumbers[11] + $selectedNumbers[14] + $selectedNumbers[17];
            $secondDigit = $secondSum % 10;
            $thirdSum = $selectedNumbers[3] + $selectedNumbers[6] + $selectedNumbers[9] + $selectedNumbers[12] + $selectedNumbers[15] + $selectedNumbers[18];
            $thirdDigit = $thirdSum % 10;
            $result = $firstDigit + $secondDigit + $thirdDigit;
            if ($result === $target) {
                $bestMatch = $selectedNumbers;
                break;
            }
        }
        return $bestMatch;
    }


    public static function getWhere(array $search = []): array
    {
        // TODO: Implement getWhere() method.
        return [];
    }
}