<?php

namespace App\Console\Commands;

use App\Notification\TelegramBot;
use App\Util;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;

/**
 * 定时检测 Google Play 上架包是否仍可访问（大致对应「未下架」）。
 *
 * 规则与 iOS 侧类似：
 * - online   : HTTP 200 且页面含正常 Play 详情特征
 * - delisted : HTTP 404，或 200 但为 Play「Not Found」错误页
 * - unknown  : 超时、429、5xx、页面过短无法判断等
 *
 * 连续 delisted / unknown 达阈值才告警；恢复 online 会清计数与已通知标记。
 */
class CheckGooglePlayStore extends Command
{
    protected $signature = 'google:check-play-store';

    protected $description = '检测 Google Play 包是否疑似下架，告警时 Telegram sendMsgAndImportant';

    /** 展示名 => Play 详情页完整 URL（须含 id= 包名） */
    protected array $apps = [
        'google-AFVegas-Nights-228' => 'https://play.google.com/store/apps/details?id=com.oipjuwenkjhde.kjhbnmuiejnd',
        'google-PrimeAF-Gaming-229' => 'https://play.google.com/store/apps/details?id=com.rewubsbdqkk.koeesowemli',
//        'google-test111' => 'https://play.google.com/store/apps/details?id=com.rewubsbdqkk.123',
    ];

    protected int $delistedConfirmTimes = 3;

    protected int $unknownConfirmTimes = 3;

    protected int $counterExpireSeconds = 3600;

    protected int $httpMaxAttempts = 3;

    protected int $sleepMinMicroseconds = 200000;

    protected int $sleepMaxMicroseconds = 500000;

    public function handle()
    {
        if ($this->apps === []) {
            $this->warn('未配置 Google Play 检测列表（CheckGooglePlayStore::$apps），跳过。');

            return 0;
        }

        $env = env('APP_ENV', 'local');

        $results = [];
        $delistedNotified = [];
        $unknownNotified = [];

        foreach ($this->apps as $name => $url) {
            $result = $this->checkSingleApp($name, $url);
            $results[] = $result;

            Util::WriteLog(
                'google_play_check',
                sprintf(
                    '[%s] %s | %s | %s',
                    strtoupper($result['status']),
                    $name,
                    $url,
                    $result['reason']
                )
            );

            $pkg = $result['package'];

            if ($result['status'] === 'online') {
                $this->clearCounters($pkg);
            } elseif ($result['status'] === 'delisted') {
                $count = $this->increaseCounter($this->getDelistedCounterKey($pkg));
                $this->clearCounter($this->getUnknownCounterKey($pkg));

                if ($count >= $this->delistedConfirmTimes) {
                    if (!$this->hasAlreadyNotified('delisted', $pkg)) {
                        $delistedNotified[] = [
                            'name'   => $name,
                            'url'    => $url,
                            'reason' => $result['reason'],
                            'count'  => $count,
                        ];
                        $this->markNotified('delisted', $pkg);
                    }
                }
            } elseif ($result['status'] === 'unknown') {
                $count = $this->increaseCounter($this->getUnknownCounterKey($pkg));
                $this->clearCounter($this->getDelistedCounterKey($pkg));

                if ($count >= $this->unknownConfirmTimes) {
                    if (!$this->hasAlreadyNotified('unknown', $pkg)) {
                        $unknownNotified[] = [
                            'name'   => $name,
                            'url'    => $url,
                            'reason' => $result['reason'],
                            'count'  => $count,
                        ];
                        $this->markNotified('unknown', $pkg);
                    }
                }
            }

            usleep(random_int($this->sleepMinMicroseconds, $this->sleepMaxMicroseconds));
        }

        if (!empty($delistedNotified)) {
            $this->notifyDelisted($env, $delistedNotified);
            $this->warn('已发送下架告警: ' . count($delistedNotified) . ' 个');
        }

        if (!empty($unknownNotified)) {
            $this->notifyUnknown($env, $unknownNotified);
            $this->warn('已发送检测异常告警: ' . count($unknownNotified) . ' 个');
        }

        foreach ($results as $item) {
            $this->line(sprintf(
                '[%s] %s - %s',
                strtoupper($item['status']),
                $item['name'],
                $item['reason']
            ));
        }

        if (empty($delistedNotified) && empty($unknownNotified)) {
            $this->info('本次检测完成，无需告警。');
        }

        return empty($delistedNotified) ? 0 : 1;
    }

    /**
     * @return array{name:string,url:string,package:string,status:string,reason:string}
     */
    protected function checkSingleApp(string $name, string $playUrl): array
    {
        $parsed = $this->parsePlayStoreUrl($playUrl);

        if ($parsed === null) {
            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => '_badurl_' . md5($playUrl),
                'status'   => 'unknown',
                'reason'   => '无法从 URL 解析包名 id=',
            ];
        }

        $package = $parsed['package'];
        $requestUrl = $this->buildDetailsUrl($package);

        $resp = $this->httpGetWithRetry($requestUrl, $this->httpMaxAttempts);

        if (!empty($resp['error'])) {
            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => $package,
                'status'   => 'unknown',
                'reason'   => '请求失败: ' . $resp['error'],
            ];
        }

        $code = (int) ($resp['code'] ?? 0);
        $body = (string) ($resp['body'] ?? '');

        if (in_array($code, [403, 429, 500, 502, 503, 504], true)) {
            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => $package,
                'status'   => 'unknown',
                'reason'   => "HTTP {$code}",
            ];
        }

        if ($code === 404) {
            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => $package,
                'status'   => 'delisted',
                'reason'   => 'Play 返回 HTTP 404（包不存在或已下架）',
            ];
        }

        if ($code !== 200) {
            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => $package,
                'status'   => 'unknown',
                'reason'   => "HTTP {$code}",
            ];
        }

        if ($this->isPlayNotFoundPage($body)) {
            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => $package,
                'status'   => 'delisted',
                'reason'   => 'Play 错误页（Not Found 等）',
            ];
        }

        if ($this->isLikelyNormalDetailPage($body)) {
            $this->clearNotifiedFlags($package);

            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => $package,
                'status'   => 'online',
                'reason'   => '详情页正常（含 Play 前端特征）',
            ];
        }

        $len = strlen($body);

        if ($len < 15000) {
            return [
                'name'     => $name,
                'url'      => $playUrl,
                'package'  => $package,
                'status'   => 'unknown',
                'reason'   => "HTTP 200 但正文过短({$len} bytes)，无法判断",
            ];
        }

        return [
            'name'     => $name,
            'url'      => $playUrl,
            'package'  => $package,
            'status'   => 'unknown',
            'reason'   => 'HTTP 200 但未识别为正常详情页（页面结构可能变更）',
        ];
    }

    protected function isPlayNotFoundPage(string $body): bool
    {
        if (stripos($body, '<title>Not Found</title>') !== false) {
            return true;
        }

        if (preg_match('/requested\s+URL\s+was\s+not\s+found/i', $body)) {
            return true;
        }

        return false;
    }

    protected function isLikelyNormalDetailPage(string $body): bool
    {
        if ($this->isPlayNotFoundPage($body)) {
            return false;
        }

        if (strlen($body) < 30000) {
            return false;
        }

        // 当前 Play Web 详情页常见内嵌数据；404 短页不含
        if (strpos($body, 'WIZ_global_data') === false) {
            return false;
        }

        return true;
    }

    /**
     * 从详情链接解析包名。
     */
    protected function parsePlayStoreUrl(string $url): ?array
    {
        $parts = parse_url($url);
        if (empty($parts['host']) || stripos($parts['host'], 'play.google.com') === false) {
            return null;
        }
        $query = $parts['query'] ?? '';
        if ($query === '') {
            return null;
        }
        parse_str($query, $q);
        if (empty($q['id']) || !is_string($q['id'])) {
            return null;
        }
        $id = $q['id'];
        if (!preg_match('/^[a-zA-Z][a-zA-Z0-9._]*$/', $id)) {
            return null;
        }

        return ['package' => $id];
    }

    protected function buildDetailsUrl(string $package): string
    {
        return 'https://play.google.com/store/apps/details?id=' . rawurlencode($package) . '&hl=en&gl=US';
    }

    /**
     * @return array{code:int,body:string,error:string}
     */
    protected function httpGetWithRetry(string $url, int $maxAttempts = 3): array
    {
        $attempt = 0;
        $last = [
            'code'  => 0,
            'body'  => '',
            'error' => '',
        ];

        while ($attempt < $maxAttempts) {
            $attempt++;

            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_TIMEOUT        => 45,
                CURLOPT_CONNECTTIMEOUT => 15,
                CURLOPT_SSL_VERIFYPEER => true,
                CURLOPT_HTTPHEADER     => [
                    'Accept: text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
                    'Accept-Language: en-US,en;q=0.9',
                    'Cache-Control: no-cache',
                ],
                CURLOPT_USERAGENT      => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
            ]);

            $body = curl_exec($ch);
            $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error = curl_error($ch);
            curl_close($ch);

            $last = [
                'code'  => $code,
                'body'  => is_string($body) ? $body : '',
                'error' => $error ?: '',
            ];

            if ($last['error'] === '' && ($code === 200 || $code === 404)) {
                return $last;
            }

            $shouldRetry = $last['error'] !== '' || in_array($code, [429, 500, 502, 503, 504], true);

            if (!$shouldRetry || $attempt >= $maxAttempts) {
                return $last;
            }

            $sleepSeconds = $attempt === 1 ? 2 : ($attempt === 2 ? 5 : 8);
            sleep($sleepSeconds);
        }

        return $last;
    }

    protected function getDelistedCounterKey(string $package): string
    {
        return 'google:play:delisted:' . $package;
    }

    protected function getUnknownCounterKey(string $package): string
    {
        return 'google:play:unknown:' . $package;
    }

    protected function getDelistedNotifiedKey(string $package): string
    {
        return 'google:play:notified:delisted:' . $package;
    }

    protected function getUnknownNotifiedKey(string $package): string
    {
        return 'google:play:notified:unknown:' . $package;
    }

    protected function increaseCounter(string $key): int
    {
        $count = (int) Redis::incr($key);
        Redis::expire($key, $this->counterExpireSeconds);

        return $count;
    }

    protected function clearCounters(string $package): void
    {
        $this->clearCounter($this->getDelistedCounterKey($package));
        $this->clearCounter($this->getUnknownCounterKey($package));
    }

    protected function clearCounter(string $key): void
    {
        try {
            Redis::del($key);
        } catch (\Throwable $e) {
            Util::WriteLog('google_play_check', 'Redis del error: ' . $e->getMessage());
        }
    }

    protected function hasAlreadyNotified(string $type, string $package): bool
    {
        try {
            $key = $type === 'delisted'
                ? $this->getDelistedNotifiedKey($package)
                : $this->getUnknownNotifiedKey($package);

            return (bool) Redis::exists($key);
        } catch (\Throwable $e) {
            Util::WriteLog('google_play_check', 'Redis exists error: ' . $e->getMessage());

            return false;
        }
    }

    protected function markNotified(string $type, string $package): void
    {
        try {
            $key = $type === 'delisted'
                ? $this->getDelistedNotifiedKey($package)
                : $this->getUnknownNotifiedKey($package);

            Redis::setex($key, $this->counterExpireSeconds, 1);
        } catch (\Throwable $e) {
            Util::WriteLog('google_play_check', 'Redis setex error: ' . $e->getMessage());
        }
    }

    protected function clearNotifiedFlags(string $package): void
    {
        try {
            Redis::del(
                $this->getDelistedNotifiedKey($package),
                $this->getUnknownNotifiedKey($package)
            );
        } catch (\Throwable $e) {
            Util::WriteLog('google_play_check', 'Redis clear notified flags error: ' . $e->getMessage());
        }
    }

    protected function notifyDelisted(string $env, array $items): void
    {
        $lines = ["[{$env}] Google Play 下架/不可访问检测告警"];

        foreach ($items as $item) {
            $lines[] = '• ' . $item['name'];
            $lines[] = '  原因: ' . $item['reason'];
            $lines[] = '  连续次数: ' . $item['count'];
            $lines[] = '  链接: ' . $item['url'];
        }

        $this->sendTelegramMessage(implode("\n", $lines));
    }

    protected function notifyUnknown(string $env, array $items): void
    {
        $lines = ["[{$env}] Google Play 检测异常告警"];

        foreach ($items as $item) {
            $lines[] = '• ' . $item['name'];
            $lines[] = '  原因: ' . $item['reason'];
            $lines[] = '  连续次数: ' . $item['count'];
            $lines[] = '  链接: ' . $item['url'];
        }

        $this->sendTelegramMessage(implode("\n", $lines));
    }

    protected function sendTelegramMessage(string $message): void
    {
        try {
            TelegramBot::getDefault()->sendMsgAndImportant($message);
        } catch (\Throwable $e) {
            Util::WriteLog('google_play_check', 'Telegram send error: ' . $e->getMessage());
            $this->error('Telegram 发送失败: ' . $e->getMessage());
        }
    }
}
