<?php

/**
 * 文件功能：婚姻核心业务服务
 *
 * 处理求婚、接受、拒绝、离婚全流程。
 * 所有金币/魅力变更通过 UserCurrencyService，
 * 亲密度变更通过 MarriageIntimacyService，
 * 参数通过 MarriageConfigService 读取。
 *
 * @author  ChatRoom Laravel
 *
 * @version 1.0.0
 */

namespace App\Services;

use App\Enums\CurrencySource;
use App\Enums\IntimacySource;
use App\Models\Marriage;
use App\Models\User;
use App\Models\UserPurchase;
use Illuminate\Support\Facades\DB;

class MarriageService
{
    public function __construct(
        private readonly UserCurrencyService $currency,
        private readonly MarriageConfigService $config,
        private readonly MarriageIntimacyService $intimacy,
    ) {}

    // ──────────────────────────── 求婚 ────────────────────────────────

    /**
     * 发起求婚。
     *
     * @param  User  $proposer  求婚方
     * @param  User  $target  被求婚方
     * @param  int  $weddingTierId  可选的婚礼档位 ID
     * @return array{ok: bool, message: string, marriage_id: int|null}
     */
    public function propose(User $proposer, User $target, int $ringPurchaseId, ?int $weddingTierId = null): array
    {
        // 不能向自己求婚
        if ($proposer->id === $target->id) {
            return ['ok' => false, 'message' => '不能向自己求婚！', 'marriage_id' => null];
        }

        // 只允许异性之间求婚（sex 字段：1=男 2=女 0=未设置）
        $validSexes = [1, 2];
        if (
            ! in_array((int) $proposer->sex, $validSexes, true) ||
            ! in_array((int) $target->sex, $validSexes, true) ||
            (int) $proposer->sex === (int) $target->sex
        ) {
            return ['ok' => false, 'message' => '只有男女双方才能互相求婚，请确认双方性别设置。', 'marriage_id' => null];
        }

        // 检查求婚方是否在冷静期
        if ($cooldownMsg = $this->checkCooldown($proposer)) {
            return ['ok' => false, 'message' => $cooldownMsg, 'marriage_id' => null];
        }

        // 检查双方是否已有进行中的婚姻
        if (Marriage::currentFor($proposer->id)) {
            return ['ok' => false, 'message' => '您已有进行中的婚姻或求婚，无法重复求婚。', 'marriage_id' => null];
        }
        if (Marriage::currentFor($target->id)) {
            return ['ok' => false, 'message' => '对方已有婚姻关系，无法向其求婚。', 'marriage_id' => null];
        }

        // 验证戒指
        $ring = UserPurchase::query()
            ->where('id', $ringPurchaseId)
            ->where('user_id', $proposer->id)
            ->where('status', 'active')
            ->whereHas('item', fn ($q) => $q->where('type', 'ring'))
            ->with('item')
            ->first();

        if (! $ring) {
            return ['ok' => false, 'message' => '未找到有效的戒指，请先到商城购买。', 'marriage_id' => null];
        }

        $expireHours = $this->config->get('proposal_expire_hours', 48);

        return DB::transaction(function () use ($proposer, $target, $ring, $expireHours, $weddingTierId) {
            // 在求婚阶段同时进行婚礼设置（由求婚方一人出全资预扣，属于 "男方付 / scheduled冻结" 模式）
            if ($weddingTierId) {
                $tier = \App\Models\WeddingTier::find($weddingTierId);
                if ($tier && $tier->is_active) {
                    if (($proposer->jjb ?? 0) < $tier->amount) {
                        return ['ok' => false, 'message' => "金币不足，该婚礼档位需要 {$tier->amount} 金币。", 'marriage_id' => null];
                    }
                }
            }

            // 戒指状态改为占用中
            $ring->update(['status' => 'used_pending']);

            // 创建婚姻记录
            $marriage = Marriage::create([
                'user_id' => $proposer->id,
                'partner_id' => $target->id,
                'ring_item_id' => $ring->shop_item_id,
                'ring_purchase_id' => $ring->id,
                'status' => 'pending',
                'proposed_at' => now(),
                'expires_at' => now()->addHours($expireHours),
                // 旧字段兼容
                'hyname' => $proposer->username,
                'hyname1' => $target->username,
            ]);

            if ($weddingTierId) {
                $weddingService = app(WeddingService::class);
                // 预扣冻结：payerType=groom, ceremonyType=scheduled
                $setupRes = $weddingService->setup($marriage, $weddingTierId, 'groom', 'scheduled');
                if (! $setupRes['ok']) {
                    throw new \Exception($setupRes['message']);
                }
            }

            return ['ok' => true, 'message' => '求婚成功，等待对方回应。', 'marriage_id' => $marriage->id];
        });
    }

    // ──────────────────────────── 接受求婚 ──────────────────────────────

    /**
     * 接受求婚，正式结婚。
     *
     * @param  Marriage  $marriage  婚姻记录（必须为 pending 状态）
     * @param  User  $acceptor  接受方（必须为 partner）
     * @return array{ok: bool, message: string}
     */
    public function accept(Marriage $marriage, User $acceptor): array
    {
        if ($marriage->status !== 'pending') {
            return ['ok' => false, 'message' => '该求婚已失效。'];
        }
        if ($marriage->partner_id !== $acceptor->id) {
            return ['ok' => false, 'message' => '无权操作此求婚。'];
        }
        if ($marriage->expires_at && $marriage->expires_at->isPast()) {
            $this->expireProposal($marriage);

            return ['ok' => false, 'message' => '求婚已超时失效，戒指已消失。'];
        }

        $ringItem = $marriage->ringItem;

        DB::transaction(function () use ($marriage, $ringItem) {
            // 正式结婚
            $marriage->update([
                'status' => 'married',
                'married_at' => now(),
                'hytime' => now(),
            ]);

            // 戒指标记已使用
            UserPurchase::where('id', $marriage->ring_purchase_id)->update(['status' => 'used']);

            // 双方各获魅力加成（通过戒指 slug 查配置）
            if ($ringItem) {
                $slug = $ringItem->slug;
                $charmKey = 'ring_'.str_replace('ring_', '', $slug).'_charm';
                $intimacyKey = 'ring_'.str_replace('ring_', '', $slug).'_intimacy';
                $charm = $this->config->get($charmKey, 50);
                $initIntimacy = $this->config->get($intimacyKey, 10);

                $proposer = $marriage->user;
                $partner = $marriage->partner;

                $ringName = $ringItem->name;
                $this->currency->change($proposer, 'charm', $charm, CurrencySource::MARRY_CHARM, "结婚魅力加成（{$ringName}）");
                $this->currency->change($partner, 'charm', $charm, CurrencySource::MARRY_CHARM, "结婚魅力加成（{$ringName}）");

                // 初始亲密度
                $this->intimacy->add($marriage, $initIntimacy, IntimacySource::WEDDING_BONUS, "结婚戒指初始亲密度（{$ringName}）", true);
            }

            // 如果有预先设置的婚礼（随求婚一起设定的），则将冻结金币转为正式扣除，并触发红包
            $ceremony = \App\Models\WeddingCeremony::where('marriage_id', $marriage->id)->where('status', 'pending')->first();
            if ($ceremony) {
                $weddingService = app(WeddingService::class);
                $weddingService->confirmCeremony($ceremony); // 解冻扣除，转为 immediate
                $weddingService->trigger($ceremony);
                broadcast(new \App\Events\WeddingCelebration($ceremony, $marriage));
            }
        });

        return ['ok' => true, 'message' => '恭喜！你们已正式结婚！'];
    }

    // ──────────────────────────── 拒绝求婚 ──────────────────────────────

    /**
     * 拒绝求婚（戒指消失，不退还）。
     *
     * @param  Marriage  $marriage  婚姻记录
     * @param  User  $rejector  拒绝方
     * @return array{ok: bool, message: string}
     */
    public function reject(Marriage $marriage, User $rejector): array
    {
        if ($marriage->status !== 'pending') {
            return ['ok' => false, 'message' => '该求婚已失效。'];
        }
        if ($marriage->partner_id !== $rejector->id) {
            return ['ok' => false, 'message' => '无权操作此求婚。'];
        }

        return DB::transaction(function () use ($marriage) {
            $marriage->update(['status' => 'rejected']);

            // 检查是否有由于求婚冻结的婚礼金币，若有则退还
            $ceremony = \App\Models\WeddingCeremony::where('marriage_id', $marriage->id)->where('status', 'pending')->first();
            if ($ceremony) {
                app(WeddingService::class)->cancelAndRefund($ceremony);
            }

            // 戒指拒绝后遗失
            UserPurchase::where('id', $marriage->ring_purchase_id)->update(['status' => 'lost']);
            // 记录戒指消失流水（amount=0，仅存档）
            if ($proposer = $marriage->user) {
                $this->currency->change($proposer, 'gold', 0, CurrencySource::RING_LOST, "求婚被拒，戒指消失（{$marriage->ringItem?->name}）");
            }

            return ['ok' => true, 'message' => '已拒绝求婚。'];
        });
    }

    // ──────────────────────────── 离婚 ──────────────────────────────────

    /**
     * 申请离婚或发起强制离婚。
     *
     * @param  Marriage  $marriage  婚姻记录（必须为 married 状态）
     * @param  User  $initiator  发起方
     * @param  string  $type  'mutual'=协议 | 'forced'=强制
     * @return array{ok: bool, message: string}
     */
    public function divorce(Marriage $marriage, User $initiator, string $type = 'mutual'): array
    {
        if ($marriage->status !== 'married') {
            return ['ok' => false, 'message' => '当前没有有效的婚姻关系。'];
        }
        if (! $marriage->involves($initiator->id)) {
            return ['ok' => false, 'message' => '无权操作此婚姻。'];
        }

        if ($type === 'forced') {
            return $this->forceDissolve($marriage, $initiator);
        }

        // 协议离婚：标记申请，等待对方确认（72h 后 Horizon 自动升级）
        $marriage->update([
            'divorce_type' => 'mutual',
            'divorcer_id' => $initiator->id,
            'divorce_requested_at' => now(),
        ]);

        return ['ok' => true, 'message' => '离婚申请已发送，等待对方确认（72小时内未回应将自动解除）。'];
    }

    /**
     * 确认协议离婚（被申请方接受）。
     *
     * @param  Marriage  $marriage  婚姻记录
     * @param  User  $confirmer  确认方（必须不是发起方）
     * @return array{ok: bool, message: string}
     */
    public function confirmDivorce(Marriage $marriage, User $confirmer): array
    {
        if ($marriage->status !== 'married' || $marriage->divorce_type !== 'mutual') {
            return ['ok' => false, 'message' => '没有待确认的离婚申请。'];
        }
        if ($marriage->divorcer_id === $confirmer->id) {
            return ['ok' => false, 'message' => '不能确认自己发起的离婚申请，如需强制离婚请另行操作。'];
        }
        if (! $marriage->involves($confirmer->id)) {
            return ['ok' => false, 'message' => '无权操作此婚姻。'];
        }

        DB::transaction(function () use ($marriage) {
            $penalty = $this->config->get('divorce_mutual_charm', 100);

            // 双方扣魅力
            $this->currency->change($marriage->user, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '协议离婚魅力惩罚');
            $this->currency->change($marriage->partner, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '协议离婚魅力惩罚');

            // 更新婚姻状态
            $marriage->update([
                'status' => 'divorced',
                'divorce_type' => 'mutual',
                'divorced_at' => now(),
                'intimacy' => 0,
                'level' => 1,
            ]);
        });

        return ['ok' => true, 'message' => '协议离婚已完成。'];
    }

    /**
     * 被申请方拒绝协议离婚申请（等同于强制离婚）。
     *
     * 规则：
     *  - 婚姻立即以 forced 类型解除
     *  - 申请人（divorcer）被扣除 divorce_forced_charm 点魅力作为惩罚
     *  - 申请人一半金币赔偿给被申请方
     *  - 申请人进入强制离婚冷静期
     *
     * @param  Marriage  $marriage  婚姻记录
     * @param  User  $respondent  被申请方（拒绝者）
     * @return array{ok: bool, message: string}
     */
    public function rejectDivorce(Marriage $marriage, User $respondent): array
    {
        if ($marriage->status !== 'married' || $marriage->divorce_type !== 'mutual') {
            return ['ok' => false, 'message' => '没有待处理的离婚申请。'];
        }
        if ($marriage->divorcer_id === $respondent->id) {
            return ['ok' => false, 'message' => '不能拒绝自己发起的离婚申请。'];
        }
        if (! $marriage->involves($respondent->id)) {
            return ['ok' => false, 'message' => '无权操作此婚姻。'];
        }

        // 申请方（被拒的一方）
        $divorcer = $marriage->user_id === $marriage->divorcer_id
            ? $marriage->user
            : $marriage->partner;

        DB::transaction(function () use ($marriage, $divorcer, $respondent) {
            $charmPenalty = $this->config->get('divorce_forced_charm', 300);

            // 申请人扣魅力
            $this->currency->change($divorcer, 'charm', -$charmPenalty, CurrencySource::DIVORCE_CHARM, '拒绝离婚时视为强制离婚，扣除魅力惩罚');

            // 申请人一半金币赔偿给对方
            $divorcerJjb = $divorcer->fresh()->jjb ?? 0;
            if ($divorcerJjb > 0) {
                $half = (int) floor($divorcerJjb / 2);
                if ($half > 0) {
                    $this->currency->change($divorcer, 'gold', -$half, CurrencySource::FORCED_DIVORCE_TRANSFER, "协议离婚被拒，赔偿一半金币给 {$respondent->username}");
                    $this->currency->change($respondent, 'gold', $half, CurrencySource::FORCED_DIVORCE_TRANSFER, "对方协议离婚被拒，获得 {$divorcer->username} 一半金币赔偿");
                }
            }

            // 更新婚姻为强制离婚
            $marriage->update([
                'status' => 'divorced',
                'divorce_type' => 'forced',
                'divorcer_id' => $divorcer->id,
                'divorced_at' => now(),
                'intimacy' => 0,
                'level' => 1,
            ]);
        });

        $divorcerJjb = $divorcer->fresh()->jjb ?? 0;
        $half = (int) floor(($divorcer->jjb ?? 0) / 2);

        return ['ok' => true, 'message' => "你已拒绝离婚申请，视为强制离婚，{$divorcer->username} 赔偿了 {$half} 枚金币给你，婚姻已解除。"];
    }

    /**
     * 强制离婚（单方立即生效，发起方金币全转对方）。
     *
     * @param  Marriage  $marriage  婚姻记录
     * @param  User  $initiator  强制发起方
     * @return array{ok: bool, message: string}
     */
    public function forceDissolve(Marriage $marriage, User $initiator, bool $byAdmin = false): array
    {
        if (! $byAdmin) {
            // 检查强制离婚间隔限制
            $limitDays = $this->config->get('forced_divorce_limit_days', 60);
            $recentForced = Marriage::query()
                ->where('divorcer_id', $initiator->id)
                ->where('divorce_type', 'forced')
                ->where('divorced_at', '>=', now()->subDays($limitDays))
                ->exists();

            if ($recentForced) {
                return ['ok' => false, 'message' => "您每{$limitDays}天内只能强制离婚1次，冷静期未满。"];
            }
        }

        $victim = $marriage->user_id === $initiator->id ? $marriage->partner : $marriage->user;

        DB::transaction(function () use ($marriage, $initiator, $victim, $byAdmin) {
            if (! $byAdmin) {
                $penalty = $this->config->get('divorce_forced_charm', 300);
                // 强制方扣魅力
                $this->currency->change($initiator, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '强制离婚魅力惩罚');

                // 金币全转对方
                $initiatorJjb = $initiator->fresh()->jjb ?? 0;
                if ($initiatorJjb > 0) {
                    $this->currency->change($initiator, 'gold', -$initiatorJjb, CurrencySource::FORCED_DIVORCE_TRANSFER, "强制离婚，财产转让给{$victim->username}");
                    $this->currency->change($victim, 'gold', $initiatorJjb, CurrencySource::FORCED_DIVORCE_TRANSFER, "强制离婚，获得{$initiator->username}全部财产");
                }
            }

            $marriage->update([
                'status' => 'divorced',
                'divorce_type' => $byAdmin ? 'admin' : 'forced',
                'divorcer_id' => $initiator->id,
                'divorced_at' => now(),
                'intimacy' => 0,
                'level' => 1,
            ]);
        });

        $msg = $byAdmin
            ? '管理员已强制解除婚姻关系。'
            : "强制离婚已完成，{$initiator->username} 的全部金币已转给 {$victim->username}。";

        return ['ok' => true, 'message' => $msg];
    }

    // ──────────────────────────── 内部工具 ────────────────────────────

    /**
     * 处理超时的求婚记录（Horizon Job 调用）。
     */
    public function expireProposal(Marriage $marriage): void
    {
        if ($marriage->status !== 'pending') {
            return;
        }

        DB::transaction(function () use ($marriage) {
            $marriage->update(['status' => 'expired']);
            UserPurchase::where('id', $marriage->ring_purchase_id)->update(['status' => 'lost']);

            if ($proposer = $marriage->user) {
                $this->currency->change($proposer, 'gold', 0, CurrencySource::RING_LOST, "求婚超时，戒指消失（{$marriage->ringItem?->name}）");
            }

            // 退还当时冻结的婚礼金币
            $ceremony = \App\Models\WeddingCeremony::where('marriage_id', $marriage->id)->where('status', 'pending')->first();
            if ($ceremony) {
                app(WeddingService::class)->cancelAndRefund($ceremony);
            }
        });
    }

    /**
     * 处理协议离婚超时自动升级为强制（Horizon Job 调用）。
     */
    public function autoExpireDivorce(Marriage $marriage): void
    {
        $divorcer = User::find($marriage->divorcer_id);
        if (! $divorcer) {
            return;
        }

        $penalty = $this->config->get('divorce_auto_charm', 150);
        DB::transaction(function () use ($marriage, $divorcer, $penalty) {
            $this->currency->change($divorcer, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '离婚申请超时，自动解除');
            $marriage->update([
                'status' => 'divorced',
                'divorce_type' => 'auto',
                'divorced_at' => now(),
                'intimacy' => 0,
                'level' => 1,
            ]);
        });
    }

    /**
     * 检查用户是否在冷静期，返回错误提示文字；无冷静期返回 null。
     */
    private function checkCooldown(User $user): ?string
    {
        // 查找最近一次离婚记录
        $lastDivorce = Marriage::query()
            ->where('status', 'divorced')
            ->where(function ($q) use ($user) {
                $q->where('user_id', $user->id)->orWhere('partner_id', $user->id);
            })
            ->orderByDesc('divorced_at')
            ->first();

        if (! $lastDivorce) {
            return null;
        }

        $type = $lastDivorce->divorce_type ?? 'mutual';

        // 强制方
        $isForcer = $lastDivorce->divorcer_id === $user->id && in_array($type, ['forced', 'auto']);
        $cooldownKey = match (true) {
            $isForcer && $type === 'forced' => 'divorce_forced_cooldown',
            $isForcer && $type === 'auto' => 'divorce_auto_cooldown',
            default => 'divorce_mutual_cooldown',
        };

        $cooldownDays = $this->config->get($cooldownKey, 70);
        $cooldownEnds = $lastDivorce->divorced_at?->addDays($cooldownDays);

        if ($cooldownEnds && $cooldownEnds->isFuture()) {
            // 取两者的完全相差天数，如果有部分不够一天的则向上取整为 1 天（例：还剩 2小时 = 1天）
            $diffInHours = now()->diffInHours($cooldownEnds);
            $remaining = max(1, (int) ceil($diffInHours / 24));

            return "您还在离婚冷静期，还需 {$remaining} 天后才能再次结婚。";
        }

        return null;
    }
}
