<?php

/**
 * 文件功能：应用服务提供者
 *
 * 负责注册和引导应用级服务：自定义 SMTP 配置动态加载、
 * 婚姻系统消息事件订阅者注册等。
 */

namespace App\Providers;

use App\Listeners\SaveMarriageSystemMessage;
use App\Models\Sysparam;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;

/**
 * 类功能：注册应用级服务与全局安全配置。
 */
class AppServiceProvider extends ServiceProvider
{
    /**
     * 注册应用级服务容器绑定。
     */
    public function register(): void
    {
        //
    }

    /**
     * 引导应用启动阶段的全局配置与事件订阅。
     */
    public function boot(): void
    {
        // 生产环境按配置强制生成 HTTPS 资源链接，避免反代链路下的 Mixed Content。
        $this->configureSecureUrls();

        // 注册登录入口限流器，阻断爆破和批量注册滥用。
        $this->registerAuthRateLimiters();

        // 注册聊天室高频动作限流器，避免消息、购买与特效广播被脚本刷爆。
        $this->registerChatActionRateLimiters();

        // 注册婚姻系统消息订阅者（结婚/婚礼/离婚通知写入聊天历史）
        Event::subscribe(SaveMarriageSystemMessage::class);

        // 动态加载自定义 SMTP 配置 (如果有数据库则执行)
        try {
            if (Schema::hasTable('sysparam')) {
                $smtpConfig = Sysparam::where('alias', 'like', 'smtp_%')->pluck('body', 'alias');

                if ($smtpConfig->isNotEmpty() && $smtpConfig->get('smtp_host')) {
                    Config::set('mail.default', 'smtp');
                    Config::set('mail.mailers.smtp', [
                        'transport' => 'smtp',
                        'host' => $smtpConfig->get('smtp_host'),
                        'port' => $smtpConfig->get('smtp_port', 465),
                        'encryption' => $smtpConfig->get('smtp_encryption', 'ssl'),
                        'username' => $smtpConfig->get('smtp_username'),
                        'password' => $smtpConfig->get('smtp_password'),
                        'timeout' => 10,
                        'local_domain' => env('MAIL_EHLO_DOMAIN'),
                    ]);

                    Config::set('mail.from', [
                        'address' => $smtpConfig->get('smtp_from_address', $smtpConfig->get('smtp_username')),
                        'name' => $smtpConfig->get('smtp_from_name', '飘落流星聊天室'),
                    ]);
                }
            }
        } catch (\Exception $e) {
            // 在安装初期表不存在时忽略，防止应用崩溃
        }
    }

    /**
     * 根据应用配置决定是否统一强制 HTTPS 方案。
     */
    private function configureSecureUrls(): void
    {
        if (! config('app.force_https')) {
            return;
        }

        URL::forceScheme('https');
    }

    /**
     * 注册聊天室前台登录与隐藏后台登录的独立限流器。
     */
    private function registerAuthRateLimiters(): void
    {
        RateLimiter::for('chat-login', function (Request $request): Limit {
            return Limit::perMinute(5)
                ->by($this->buildAuthRateLimitKey($request, 'chat-login'))
                ->response(function (Request $request, array $headers) {
                    return response()->json([
                        'status' => 'error',
                        'message' => '登录尝试过于频繁，请 1 分钟后再试。',
                    ], 429, $headers);
                });
        });

        RateLimiter::for('admin-hidden-login', function (Request $request): Limit {
            return Limit::perMinute(5)
                ->by($this->buildAuthRateLimitKey($request, 'admin-hidden-login'))
                ->response(function (Request $request, array $headers) {
                    $response = redirect()->route('admin.login')
                        ->withInput($request->except(['password', 'captcha']))
                        ->withErrors(['username' => '登录尝试过于频繁，请 1 分钟后再试。']);

                    foreach ($headers as $headerName => $headerValue) {
                        $response->headers->set($headerName, $headerValue);
                    }

                    $response->setStatusCode(429);

                    return $response;
                });
        });
    }

    /**
     * 构造登录限流键，按场景 + 用户名 + IP 维度隔离计数。
     */
    private function buildAuthRateLimitKey(Request $request, string $scene): string
    {
        $username = Str::lower(trim((string) $request->input('username', 'guest')));

        return implode('|', [$scene, $username, $request->ip()]);
    }

    /**
     * 注册聊天室内高频动作限流器。
     */
    private function registerChatActionRateLimiters(): void
    {
        RateLimiter::for('chat-send', function (Request $request): Limit {
            return Limit::perMinute(40)
                ->by($this->buildChatActionRateLimitKey($request, 'chat-send'));
        });

        RateLimiter::for('chat-shop-buy', function (Request $request): Limit {
            return Limit::perMinute(20)
                ->by($this->buildChatActionRateLimitKey($request, 'chat-shop-buy'));
        });

        RateLimiter::for('chat-effect', function (Request $request): Limit {
            return Limit::perMinute(6)
                ->by($this->buildChatActionRateLimitKey($request, 'chat-effect'));
        });
    }

    /**
     * 构造聊天室动作限流键，按场景、用户与房间隔离计数。
     */
    private function buildChatActionRateLimitKey(Request $request, string $scene): string
    {
        $userId = (string) ($request->user()?->id ?? 'guest');
        $roomId = (string) ($request->route('id') ?? $request->input('room_id', 'global'));

        return implode('|', [$scene, $userId, $roomId, $request->ip()]);
    }
}
