package com.njcn.gather.detection.controller;

import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.constant.OperateType;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.LogUtil;
import com.njcn.gather.detection.lock.DetectionLock;
import com.njcn.gather.detection.lock.DetectionLockManager;
import com.njcn.gather.detection.lock.DetectionLockManager.AcquireResult;
import com.njcn.gather.detection.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.detection.pojo.param.ClosePreTestParam;
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
import com.njcn.gather.detection.pojo.param.FormalProgressParam;
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
import com.njcn.gather.detection.pojo.param.SimulateDetectionParam;
import com.njcn.gather.detection.pojo.vo.DetectionLockHolderVO;
import com.njcn.gather.detection.pojo.vo.FormalProgressVO;
import com.njcn.gather.detection.service.PreDetectionService;
import com.njcn.gather.detection.util.socket.FormalTestManager;
import com.njcn.gather.user.user.pojo.po.SysUser;
import com.njcn.gather.user.user.service.ISysUserService;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import com.njcn.web.utils.RequestUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;


/**
 * @author chendaofei
 */
@Slf4j
@Api(tags = "预检测")
@RestController
@RequestMapping("/prepare")
@RequiredArgsConstructor
public class PreDetectionController extends BaseController {

    private final PreDetectionService preDetectionService;
    private final ISysUserService sysUserService;

    /**
     * 开始检测通用入口
     *
     * @param param 实体参数
     */
    @PostMapping("/startPreTest")
    @OperateInfo
    @ApiOperation("开始检测")
    @ApiImplicitParam(name = "param", value = "查询参数", required = true)
    public HttpResult<?> startPreTest(@RequestBody @Validated PreDetectionParam param) {
        String methodDescribe = getMethodDescribe("startPreTest");
        HttpResult<DetectionLockHolderVO> busy = tryAcquireLock(param.getUserPageId());
        if (busy != null) {
            return busy;
        }
        // 同步阶段抛异常时回滚锁（PLAN_AND_SOURCE_NOT / SOURCE_INFO_NOT 等业务异常会被全局处理器吞掉，
        // 锁会卡在用户手上直到 4 小时超时，故需 finally 兜底）
        boolean keepLock = false;
        try {
            // 重置 FormalTestManager 暂停计数残留，避免上次暂停残留计数误触发 R4
            FormalTestManager.clearPauseState();
            preDetectionService.sourceCommunicationCheck(param);
            keepLock = true;
            return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
        } finally {
            if (!keepLock) {
                releaseLockSelf("START_PRE_SYNC_FAILED");
            }
        }
    }


    /**
     * 程控源-源通讯校验
     *
     * @param param
     * @return
     */
    @PostMapping("/ytxCheckSimulate")
    @OperateInfo
    @ApiOperation("源通讯校验")
    @ApiImplicitParam(name = "param", value = "查询参数", required = true)
    public HttpResult<?> ytxCheckSimulate(@RequestBody @Validated SimulateDetectionParam param) {
        String methodDescribe = getMethodDescribe("ytxCheckSimulate");
        HttpResult<DetectionLockHolderVO> busy = requireFreeOrSelf();
        if (busy != null) {
            return busy;
        }
        preDetectionService.ytxCheckSimulate(param);
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
    }

    /**
     * 程控源检测
     */
    @PostMapping("/startSimulateTest")
    @OperateInfo
    @ApiOperation("启动")
    @ApiImplicitParam(name = "param", value = "查询参数", required = true)
    public HttpResult<?> startTestSimulate(@RequestBody @Validated SimulateDetectionParam param) {
        String methodDescribe = getMethodDescribe("startSimulateTest");
        // ContrastDetectionParam 无 userPageId 字段，用 loginName 作为会话标识（与 WS 会话 key 一致）
        HttpResult<DetectionLockHolderVO> busy = tryAcquireLock(param.getUserPageId());
        if (busy != null) {
            return busy;
        }
        // 同步阶段抛异常时回滚锁，理由同 startPreTest
        boolean keepLock = false;
        try {
            FormalTestManager.clearPauseState();
            preDetectionService.sendScript(param);
            keepLock = true;
            return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
        } finally {
            if (!keepLock) {
                releaseLockSelf("START_SIMULATE_TEST_SYNC_FAILED");
            }
        }
    }

    /**
     * 关闭检测-程控源
     */
    @PostMapping("/closeSimulateTest")
    @OperateInfo
    @ApiOperation("停止")
    @ApiImplicitParam(name = "param", value = "查询参数", required = true)
    public HttpResult<?> closeSimulateTest(@RequestBody @Validated SimulateDetectionParam param) {
        String methodDescribe = getMethodDescribe("closeSimulateTest");
        HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
        if (busy != null) {
            return busy;
        }
        try {
            preDetectionService.closeTestSimulate(param);
        } finally {
            // 即使业务异常也要释放锁，避免锁残留导致他人无法接手
            releaseLockSelf("USER_STOP");
        }
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
    }


    /**
     * 系数校验
     */
    @PostMapping("/coefficientCheck")
    @OperateInfo
    @ApiOperation("系数校验")
    @ApiImplicitParam(name = "param", value = "查询参数", required = true)
    public HttpResult<?> coefficientCheck(@RequestBody PreDetectionParam param) {
        String methodDescribe = getMethodDescribe("coefficientCheck");
        HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
        if (busy != null) {
            return busy;
        }
        preDetectionService.coefficientCheck(param);
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
    }


    /**
     * 暂停检测，兼容旧前端：默认等待当前小项完成后暂停。
     */
    @GetMapping("/closePreTest")
    @OperateInfo
    @ApiOperation("暂停检测")
    public HttpResult<?> temStopTest() {
        return closePreTest(new ClosePreTestParam());
    }

    /**
     * 暂停检测
     */
    @PostMapping("/closePreTest")
    @OperateInfo
    @ApiOperation("暂停检测")
    public HttpResult<?> closePreTest(@RequestBody(required = false) ClosePreTestParam param) {
        String methodDescribe = getMethodDescribe("temStopTest");
        HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
        if (busy != null) {
            return busy;
        }
        // 暂停保持锁（spec §2.3），不释放
        ClosePreTestParam request = param == null ? new ClosePreTestParam() : param;
        preDetectionService.temStopTest(RequestUtil.getLoginNameByToken(), request.isWaitCurrentItem());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
    }

    /**
     * 重新开始检测
     */
    @PostMapping("/restartTemTest")
    @OperateInfo
    @ApiOperation("重新开始检测")
    @ApiImplicitParam(name = "param", value = "参数", required = true)
    public HttpResult<?> restartTemTest(@RequestBody PreDetectionParam param) {
        String methodDescribe = getMethodDescribe("restartTemTest");
        HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
        if (busy != null) {
            return busy;
        }
        preDetectionService.restartTemTest(param);
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
    }

    @PostMapping("/saveFormalProgress")
    @OperateInfo
    @ApiOperation("保存正式检测暂存进度")
    public HttpResult<?> saveFormalProgress(@RequestBody @Validated FormalProgressParam param) {
        String methodDescribe = getMethodDescribe("saveFormalProgress");
        HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
        if (busy != null) {
            return busy;
        }
        try {
            preDetectionService.saveFormalProgress(param);
        } finally {
            releaseLockSelf("SAVE_FORMAL_PROGRESS");
        }
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
    }

    @PostMapping("/formalProgress")
    @OperateInfo
    @ApiOperation("查询正式检测暂存进度并放行续检")
    public HttpResult<?> formalProgress(@RequestBody @Validated FormalProgressParam param) {
        String methodDescribe = getMethodDescribe("formalProgress");
        HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
        if (busy != null) {
            return busy;
        }
        FormalProgressVO data = preDetectionService.formalProgress(param);
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, data, methodDescribe);
    }


    /**
     * 开始比对检测（包括预检测、正式检测）通用入口
     */
    @PostMapping("/startContrastTest")
    @OperateInfo
    @ApiOperation("开始比对检测")
    @ApiImplicitParam(name = "param", value = "查询参数", required = true)
    public HttpResult<?> startContrastTest(@RequestBody @Validated ContrastDetectionParam param) {
        String methodDescribe = getMethodDescribe("startContrastTest");
        // ContrastDetectionParam 无 userPageId 字段，用 loginName 作为会话标识（与 WS 会话 key 一致）
        HttpResult<DetectionLockHolderVO> busy = tryAcquireLock(param.getLoginName());
        if (busy != null) {
            return busy;
        }
        // 同步阶段抛异常时回滚锁，理由同 startPreTest
        boolean keepLock = false;
        try {
            FormalTestManager.clearPauseState();
            preDetectionService.startContrastTest(param);
            keepLock = true;
            return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
        } finally {
            if (!keepLock) {
                releaseLockSelf("START_CONTRAST_SYNC_FAILED");
            }
        }
    }


    @OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.DOWNLOAD)
    @PostMapping("/exportAlignData")
    @ApiOperation("实时对齐数据导出为csv文件")
    public void exportAlignData() {
        String methodDescribe = getMethodDescribe("exportAlignData");
        LogUtil.njcnDebug(log, "{}", methodDescribe);
        preDetectionService.exportAlignData();
    }

    @OperateInfo(info = LogEnum.SYSTEM_COMMON)
    @GetMapping("/canCoefficient")
    @ApiOperation("比对模式是否能够进行系数校验")
    public HttpResult<Boolean> canCoefficient() {
        String methodDescribe = getMethodDescribe("canCoefficient");
        LogUtil.njcnDebug(log, "{}", methodDescribe);

        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, preDetectionService.getCanCoefficient(), methodDescribe);
    }

    @OperateInfo(info = LogEnum.SYSTEM_COMMON)
    @GetMapping("/startCoefficient")
    @ApiOperation("比对模式开启系数校验")
    public HttpResult<?> startCoefficient() {
        String methodDescribe = getMethodDescribe("startCoefficient");
        LogUtil.njcnDebug(log, "{}", methodDescribe);
        HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
        if (busy != null) {
            return busy;
        }
        preDetectionService.startCoefficient();
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
    }

    // ============ 检测互斥锁辅助方法 ============

    /** 抢锁入口（startPreTest / startContrastTest 用）。
     *  抢到→null；被他人持有或竞态失败→返回 busy 响应；
     *  使用方：拿到非 null 返回值直接 return 给上层。 */
    private HttpResult<DetectionLockHolderVO> tryAcquireLock(String userPageId) {
        String userId = RequestUtil.getUserId();
        AcquireResult r = DetectionLockManager.getInstance()
                .tryAcquire(userId, resolveDisplayName(userId), userPageId);
        if (r.isOk()) {
            return null;
        }
        return HttpResultUtil.assembleResult(
                DetectionResponseEnum.DETECTION_BUSY.getCode(),
                r.getHolder(),
                DetectionResponseEnum.DETECTION_BUSY.getMessage());
    }

    /** 中间接口校验：要求当前 holder == 自己。
     *  空闲 → 返回 busy data=null（"请先开始检测"语义）；
     *  他人持有 → 返回 busy + holder；
     *  自己持有 → 返回 null（放行）。 */
    private HttpResult<DetectionLockHolderVO> requireHolderSelf() {
        DetectionLock cur = DetectionLockManager.getInstance().getCurrent();
        String me = RequestUtil.getUserId();
        if (cur != null && me.equals(cur.getUserId())) {
            return null;
        }
        DetectionLockHolderVO holder = cur == null ? null : DetectionLockManager.toHolderVO(cur);
        return HttpResultUtil.assembleResult(
                DetectionResponseEnum.DETECTION_BUSY.getCode(),
                holder,
                DetectionResponseEnum.DETECTION_BUSY.getMessage());
    }

    /** 辅助接口规则（ytxCheckSimulate）：锁空闲 → 放行；他人持有 → busy；自己持有 → 放行。 */
    private HttpResult<DetectionLockHolderVO> requireFreeOrSelf() {
        DetectionLock cur = DetectionLockManager.getInstance().getCurrent();
        if (cur == null) {
            return null;
        }
        String me = RequestUtil.getUserId();
        if (me.equals(cur.getUserId())) {
            return null;
        }
        return HttpResultUtil.assembleResult(
                DetectionResponseEnum.DETECTION_BUSY.getCode(),
                DetectionLockManager.toHolderVO(cur),
                DetectionResponseEnum.DETECTION_BUSY.getMessage());
    }

    /** 释放锁（用户主动终止）。 */
    private void releaseLockSelf(String reason) {
        DetectionLockManager.getInstance().releaseIfHeldBy(RequestUtil.getUserId(), reason);
    }

    /** 解析展示给前端的用户名（昵称优先，loginName 兜底，避免 BUSY 弹窗显示 "unknown user"）。 */
    private String resolveDisplayName(String userId) {
        if (StrUtil.isBlank(userId)) {
            return "";
        }
        try {
            SysUser user = sysUserService.getById(userId);
            if (user != null && StrUtil.isNotBlank(user.getName())) {
                return user.getName();
            }
            if (user != null && StrUtil.isNotBlank(user.getLoginName())) {
                return user.getLoginName();
            }
        } catch (Exception e) {
            log.warn("解析检测锁持有者昵称失败，userId={}", userId, e);
        }
        // 最终兜底：用 token 里的 loginName，不要返回 "unknown user"
        String loginName = RequestUtil.getLoginNameByToken();
        return StrUtil.isNotBlank(loginName) ? loginName : userId;
    }
}
