<?php

/**
 * 文件功能：职务任命服务
 * 处理职务系统的核心业务逻辑：任命、撤销、权限校验
 * 所有权限操作均写入 position_authority_logs 留存审计
 *
 * @author   ChatRoom Laravel
 *
 * @version  1.0.0
 */

namespace App\Services;

use App\Models\Position;
use App\Models\PositionAuthorityLog;
use App\Models\User;
use App\Models\UserPosition;
use Illuminate\Support\Facades\DB;

class AppointmentService
{
    /**
     * 获取用户当前在职记录（无则返回 null）
     */
    public function getActivePosition(User $user): ?UserPosition
    {
        return UserPosition::query()
            ->where('user_id', $user->id)
            ->where('is_active', true)
            ->with(['position.department', 'position.appointablePositions'])
            ->first();
    }

    /**
     * 校验操作人是否有权将目标用户任命到指定职务
     *
     * id=1 超级管理员绕过所有要目校验，可直接任命任意职务。
     *
     * @return array{ok: bool, message: string}
     */
    public function validateAppoint(User $operator, User $target, Position $targetPosition): array
    {
        // 超级管理员（id=1）特权：跳过职务和白名单校验，只检查被任命人是否已有职务
        if ($operator->id === 1) {
            $existingPosition = $this->getActivePosition($target);
            if ($existingPosition) {
                $currentName = $existingPosition->position->name;

                return ['ok' => false, 'message' => "【{$target->username}】当前已担任【{$currentName}】，请先撤销其职务再重新任命。"];
            }

            return ['ok' => true, 'message' => '超级管理员直接授权'];
        }

        // 操作人必须有在职职务
        $operatorPosition = $this->getActivePosition($operator);
        if (! $operatorPosition) {
            return ['ok' => false, 'message' => '您当前无在职职务，无法进行任命操作。'];
        }

        // 校验任命白名单：目标职务是否在操作人职务的可任命列表内
        $isAllowed = $operatorPosition->position
            ->appointablePositions()
            ->where('positions.id', $targetPosition->id)
            ->exists();

        if (! $isAllowed) {
            return ['ok' => false, 'message' => "您的职务无权任命【{$targetPosition->name}】职位。"];
        }

        // 检查目标职务是否已满员
        if ($targetPosition->isFull()) {
            return ['ok' => false, 'message' => "【{$targetPosition->name}】职位人数已满，无法继续任命。"];
        }

        // 检查被任命人是否已有在职职务
        $existingPosition = $this->getActivePosition($target);
        if ($existingPosition) {
            $currentName = $existingPosition->position->name;

            return ['ok' => false, 'message' => "【{$target->username}】当前已担任【{$currentName}】，请先撤销其职务再重新任命。"];
        }

        return ['ok' => true, 'message' => '校验通过'];
    }

    /**
     * 执行任命操作
     * 任命成功后自动同步 user_level 并写入权限日志
     *
     * @return array{ok: bool, message: string, userPosition?: UserPosition}
     */
    public function appoint(User $operator, User $target, Position $targetPosition, ?string $remark = null): array
    {
        // 权限校验
        $validation = $this->validateAppoint($operator, $target, $targetPosition);
        if (! $validation['ok']) {
            return $validation;
        }

        // id=1 超级管理员无需在职职务，直接任命
        $operatorPosition = $operator->id === 1 ? null : $this->getActivePosition($operator);

        DB::transaction(function () use ($operator, $target, $targetPosition, $remark, $operatorPosition, &$userPosition) {
            // 创建任职记录
            $userPosition = UserPosition::create([
                'user_id' => $target->id,
                'position_id' => $targetPosition->id,
                'appointed_by_user_id' => $operator->id,
                'appointed_at' => now(),
                'remark' => $remark,
                'is_active' => true,
            ]);

            // 同步 user_level
            $target->update(['user_level' => $targetPosition->level]);

            // 写入权限操作日志
            $this->logAuthority(
                operator: $operator,
                operatorPosition: $operatorPosition,
                actionType: 'appoint',
                target: $target,
                targetPosition: $targetPosition,
                remark: $remark
            );
        });

        return [
            'ok' => true,
            'message' => "已成功将【{$target->username}】任命为【{$targetPosition->name}】。",
            'userPosition' => $userPosition,
        ];
    }

    /**
     * 校验操作人是否有权撤销目标用户的职务
     *
     * id=1 超级管理员可直接撤销任意职务。
     *
     * @return array{ok: bool, message: string}
     */
    public function validateRevoke(User $operator, User $target): array
    {
        // 超级管理员（id=1）特权：跳过白名单校验，直接撤销任意职务
        if ($operator->id === 1) {
            $targetPosition = $this->getActivePosition($target);
            if (! $targetPosition) {
                return ['ok' => false, 'message' => "【{$target->username}】当前没有在职职务。"];
            }

            return ['ok' => true, 'message' => '超级管理员直接授权'];
        }

        // 操作人必须有在职职务
        $operatorPosition = $this->getActivePosition($operator);
        if (! $operatorPosition) {
            return ['ok' => false, 'message' => '您当前无在职职务，无法进行撤职操作。'];
        }

        // 被撤销人必须有在职职务
        $targetPosition = $this->getActivePosition($target);
        if (! $targetPosition) {
            return ['ok' => false, 'message' => "【{$target->username}】当前没有在职职务。"];
        }

        // 操作人不能撤销自己
        if ($operator->id === $target->id) {
            return ['ok' => false, 'message' => '不能撤销自己的职务。'];
        }

        // 操作人的任命白名单中必须包含目标职务（即有权任命该职务，也就有权撤销）
        $isAllowed = $operatorPosition->position
            ->appointablePositions()
            ->where('positions.id', $targetPosition->position_id)
            ->exists();

        if (! $isAllowed) {
            return ['ok' => false, 'message' => "您的职务无权撤销【{$targetPosition->position->name}】职位的人员。"];
        }

        return ['ok' => true, 'message' => '校验通过'];
    }

    /**
     * 执行撤销职务操作
     * 撤销后 user_level 归 1，并写入权限日志
     *
     * @return array{ok: bool, message: string}
     */
    public function revoke(User $operator, User $target, ?string $remark = null): array
    {
        // 权限校验
        $validation = $this->validateRevoke($operator, $target);
        if (! $validation['ok']) {
            return $validation;
        }

        $operatorPosition = $this->getActivePosition($operator);
        $targetUP = $this->getActivePosition($target);

        DB::transaction(function () use ($operator, $target, $remark, $operatorPosition, $targetUP) {
            // 撤销在职记录
            $targetUP->update([
                'is_active' => false,
                'revoked_at' => now(),
                'revoked_by_user_id' => $operator->id,
            ]);

            // 关闭尚未结束的 duty_log（只结算今日，历史遗留置0）
            $target->activePosition?->dutyLogs()
                ->whereNull('logout_at')
                ->whereDate('login_at', today())
                ->update([
                    'logout_at' => now(),
                    'duration_seconds' => DB::raw('TIMESTAMPDIFF(SECOND, login_at, NOW())'),
                ]);

            // 历史遗留未关闭日志直接清零
            $target->activePosition?->dutyLogs()
                ->whereNull('logout_at')
                ->whereDate('login_at', '<', today())
                ->update([
                    'logout_at' => DB::raw('login_at'),
                    'duration_seconds' => 0,
                ]);

            // 撤职后：按当前经验值重新计算等级（不再无条件归 1）
            // 这样用户撤职后能保留正常的经验升级成果
            $recalcLevel = \App\Models\Sysparam::calculateLevel($target->exp_num ?? 0);
            $superLevel = (int) \App\Models\Sysparam::getValue('superlevel', '100');
            // 不超过满级阈值
            $safeLevel = max(1, min($recalcLevel, $superLevel - 1));
            $target->update(['user_level' => $safeLevel]);

            // 写入权限操作日志
            $this->logAuthority(
                operator: $operator,
                operatorPosition: $operatorPosition,
                actionType: 'revoke',
                target: $target,
                targetPosition: $targetUP->position,
                remark: $remark
            );
        });

        return [
            'ok' => true,
            'message' => "已成功撤销【{$target->username}】的【{$targetUP->position->name}】职务，其等级已归 1。",
        ];
    }

    /**
     * 记录权限操作日志（各类管理操作公共调用）
     *
     * @param  string  $actionType  操作类型（appoint/revoke/reward/warn/kick/mute/banip/other）
     */
    public function logAuthority(
        User $operator,
        ?UserPosition $operatorPosition,
        string $actionType,
        User $target,
        ?Position $targetPosition = null,
        ?int $amount = null,
        ?string $remark = null,
    ): void {
        // 无在职职务的操作不记录（普通管理员通过 user_level 操作不进此表）
        if (! $operatorPosition) {
            return;
        }

        PositionAuthorityLog::create([
            'user_id' => $operator->id,
            'user_position_id' => $operatorPosition->id,
            'action_type' => $actionType,
            'target_user_id' => $target->id,
            'target_position_id' => $targetPosition?->id,
            'amount' => $amount,
            'remark' => $remark,
        ]);
    }

    /**
     * 获取视图用：操作人有权任命的职务列表（用于后台/弹窗任命下拉选择）
     *
     * @return \Illuminate\Database\Eloquent\Collection<int, Position>
     */
    public function getAppointablePositions(User $operator)
    {
        $operatorPosition = $this->getActivePosition($operator);
        if (! $operatorPosition) {
            return collect();
        }

        return $operatorPosition->position
            ->appointablePositions()
            ->with('department')
            ->orderByDesc('rank')
            ->get();
    }
}
