<?php

/**
 * 文件功能：婚姻亲密度操作服务
 *
 * 统一管理婚姻亲密度的增减逻辑：
 * - 写入 marriage_intimacy_logs 日志
 * - 更新 marriages.intimacy 和 marriages.level
 * - 通过 Redis 计数器控制每日来源上限
 * - 所有参数从 MarriageConfigService 读取，不硬编码
 *
 * @author  ChatRoom Laravel
 *
 * @version 1.0.0
 */

namespace App\Services;

use App\Enums\IntimacySource;
use App\Models\Marriage;
use App\Models\MarriageIntimacyLog;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class MarriageIntimacyService
{
    public function __construct(
        private readonly MarriageConfigService $config,
    ) {}

    /**
     * 增加婚姻亲密度（带每日上限检查）。
     *
     * @param  Marriage  $marriage  婚姻记录
     * @param  int  $amount  增加量（必须为正整数）
     * @param  IntimacySource  $source  来源枚举
     * @param  string  $remark  备注
     * @param  bool  $skipCap  是否跳过每日上限（管理员调整/结婚初始时传 true）
     */
    public function add(
        Marriage $marriage,
        int $amount,
        IntimacySource $source,
        string $remark = '',
        bool $skipCap = false,
    ): void {
        if ($amount <= 0) {
            return;
        }

        // 每日上限检查（管理员调整和结婚初始加成跳过）
        if (! $skipCap) {
            $cap = $this->getDailyCap($source);
            if ($cap > 0 && ! $this->checkAndIncrRedis($marriage->id, $source, $amount, $cap)) {
                return; // 已达到每日上限，静默忽略
            }
        }

        DB::transaction(function () use ($marriage, $amount, $source, $remark) {
            // 原子加亲密度
            $marriage->increment('intimacy', $amount);
            $newIntimacy = $marriage->fresh()->intimacy;

            // 更新婚姻等级
            $newLevel = self::calcLevel($newIntimacy, $this->config);
            if ($newLevel !== $marriage->level) {
                $marriage->update(['level' => $newLevel]);
            }

            // 写入日志
            MarriageIntimacyLog::create([
                'marriage_id' => $marriage->id,
                'amount' => $amount,
                'balance_after' => $newIntimacy,
                'source' => $source->value,
                'remark' => $remark,
            ]);
        });
    }

    /**
     * 每日结婚天数批量加亲密度（ProcessMarriageIntimacy Job 调用）。
     * 给所有 married 状态的婚姻对加 intimacy_daily_time 配置的积分。
     */
    public function dailyBatch(): void
    {
        $amount = $this->config->get('intimacy_daily_time', 10);
        $remark = '每日结婚天数奖励';

        Marriage::query()
            ->where('status', 'married')
            ->cursor()
            ->each(function (Marriage $marriage) use ($amount, $remark) {
                $this->add($marriage, $amount, IntimacySource::DAILY_TIME, $remark, true);
            });
    }

    /**
     * 双方同时在线每分钟加亲密度（AutoSaveJob 调用）。
     */
    public function onlineTick(Marriage $marriage): void
    {
        $amount = $this->config->get('intimacy_online_per_min', 1);
        $this->add($marriage, $amount, IntimacySource::ONLINE_TOGETHER, '双方同时在线');
    }

    /**
     * 送花触发亲密度（GiftController 调用）。
     * 自动判断当前用户是送花方还是收花方，并使用对应加成。
     *
     * @param  Marriage  $marriage  婚姻关系
     * @param  int  $giverId  送花者 user.id
     * @param  int  $flowerQty  送花数量
     */
    public function onFlowerSent(Marriage $marriage, int $giverId, int $flowerQty = 1): void
    {
        // 送花方：send_flower 加成
        $sendAmount = $this->config->get('intimacy_send_flower', 1) * $flowerQty;
        $this->add($marriage, $sendAmount, IntimacySource::SEND_FLOWER, "向伴侣送花×{$flowerQty}");

        // 收花方（两人共享同一段婚姻记录，无需区分）：recv_flower 加成
        $recvAmount = $this->config->get('intimacy_recv_flower', 2) * $flowerQty;
        $this->add($marriage, $recvAmount, IntimacySource::RECV_FLOWER, "伴侣送花×{$flowerQty}");
    }

    /**
     * 私聊消息触发亲密度（WhisperController 调用）。
     * 使用 Redis 计数，每 2 条触发 1 次加分。
     *
     * @param  Marriage  $marriage  婚姻关系
     */
    public function onPrivateChat(Marriage $marriage): void
    {
        $redisKey = "marriage:{$marriage->id}:whisper_count:".now()->toDateString();
        $count = Redis::incr($redisKey);

        // 设置 TTL（次日 00:05 过期）
        if ($count === 1) {
            Redis::expireAt($redisKey, Carbon::tomorrow()->addMinutes(5)->timestamp);
        }

        // 每2条触发1次
        if ($count % 2 === 0) {
            $amount = $this->config->get('intimacy_private_chat', 1);
            $this->add($marriage, $amount, IntimacySource::PRIVATE_CHAT, '私聊消息');
        }
    }

    /**
     * 根据亲密度计算婚姻等级（1-4）。
     *
     * @param  int  $intimacy  当前亲密度
     * @param  MarriageConfigService  $config  配置服务实例
     */
    public static function calcLevel(int $intimacy, MarriageConfigService $config): int
    {
        if ($intimacy >= $config->get('level4_threshold', 1500)) {
            return 4;
        }
        if ($intimacy >= $config->get('level3_threshold', 600)) {
            return 3;
        }
        if ($intimacy >= $config->get('level2_threshold', 200)) {
            return 2;
        }

        return 1;
    }

    /**
     * 返回等级图标（用于前端展示）。
     */
    public static function levelIcon(int $level): string
    {
        return match ($level) {
            2 => '💕',
            3 => '💞',
            4 => '👑',
            default => '💑',
        };
    }

    /**
     * 返回等级名称。
     */
    public static function levelName(int $level): string
    {
        return match ($level) {
            2 => '恩爱夫妻',
            3 => '情深意重',
            4 => '白头偕老',
            default => '新婚燕尔',
        };
    }

    /**
     * 获取指定来源的每日上限配置值（0=不限）。
     */
    private function getDailyCap(IntimacySource $source): int
    {
        return match ($source) {
            IntimacySource::ONLINE_TOGETHER => $this->config->get('intimacy_online_daily_cap', 120),
            IntimacySource::RECV_FLOWER => $this->config->get('intimacy_recv_flower_cap', 40),
            IntimacySource::SEND_FLOWER => $this->config->get('intimacy_send_flower_cap', 20),
            IntimacySource::PRIVATE_CHAT => $this->config->get('intimacy_private_chat_cap', 10),
            default => 0, // 不限
        };
    }

    /**
     * 检查 Redis 每日计数器是否超限，未超限则增加计数。
     * 返回 true 表示可以继续加分，false 表示已达上限。
     *
     * @param  int  $marriageId  婚姻 ID
     * @param  IntimacySource  $source  来源
     * @param  int  $amount  本次加分量
     * @param  int  $cap  每日上限
     */
    private function checkAndIncrRedis(int $marriageId, IntimacySource $source, int $amount, int $cap): bool
    {
        $redisKey = "marriage:{$marriageId}:intimacy:{$source->value}:".now()->toDateString();
        $current = (int) Redis::get($redisKey);

        if ($current >= $cap) {
            return false; // 已达上限
        }

        // 本次可加量（不超过剩余额度）
        $actualAdd = min($amount, $cap - $current);
        Redis::incrBy($redisKey, $actualAdd);

        // 设置 TTL（次日 00:05 过期）
        if ($current === 0) {
            Redis::expireAt($redisKey, Carbon::tomorrow()->addMinutes(5)->timestamp);
        }

        return true;
    }
}
