package com.yd.csf.service.audit;

import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.csf.service.model.SysOperLog;
import com.yd.csf.service.service.AuditLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.UUID;

@Slf4j
@Component
@RequiredArgsConstructor
public class AuditService {

    private final AuditLogService operLogService;

    /**
     * 开始审计，创建新的审计上下文。
     *
     * @param module       业务模块
     * @param businessType 业务类型
     * @param content      操作描述
     * @return requestId
     */
    public String begin(String module, Integer businessType, String content) {
        String requestId = UUID.randomUUID().toString().replace("-", "");

        AuditContext ctx = new AuditContext();
        ctx.setRequestId(requestId);
        ctx.setModule(module);
        ctx.setBusinessType(businessType);
        ctx.setContent(content);
        ctx.setOperTime(new Date());

        fillRequestInfo(ctx);
        fillOperatorInfo(ctx);

        AuditContextHolder.set(ctx);
        return requestId;
    }

    /**
     * 使用已有 requestId 恢复审计上下文（用于异步回调）。
     */
    public void begin(String requestId) {
        AuditContext ctx = new AuditContext();
        ctx.setRequestId(requestId);
        ctx.setOperTime(new Date());
        AuditContextHolder.set(ctx);
    }

    /**
     * 标记操作成功，写入 sys_oper_log。
     */
    public void success(String requestId) {
        AuditContext ctx = AuditContextHolder.get();
        if (ctx == null) {
            return;
        }
        SysOperLog log = buildOperLog(ctx);
        log.setStatus(0);
        operLogService.save(log);
    }

    /**
     * 标记操作失败，写入 sys_oper_log。
     */
    public void fail(String requestId, String errorMsg) {
        AuditContext ctx = AuditContextHolder.get();
        if (ctx == null) {
            return;
        }
        SysOperLog log = buildOperLog(ctx);
        log.setStatus(1);
        log.setErrorMsg(errorMsg);
        operLogService.save(log);
    }

    /**
     * 关闭审计，清理 ThreadLocal。
     */
    public void close(String requestId) {
        AuditContextHolder.clear();
    }

    private void fillRequestInfo(AuditContext ctx) {
        try {
            ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attrs != null) {
                HttpServletRequest request = attrs.getRequest();
                ctx.setOperUrl(request.getRequestURI());
                ctx.setOperIp(request.getRemoteAddr());
            }
        } catch (Exception e) {
            log.debug("获取请求信息失败: {}", e.getMessage());
        }
    }

    private void fillOperatorInfo(AuditContext ctx) {
        try {
            AuthUserDto user = SecurityUtil.getCurrentLoginUser();
            ctx.setUserId(user.getId());
            ctx.setOperName(user.getUsername());
        } catch (Exception e) {
            log.debug("获取操作人信息失败: {}", e.getMessage());
        }
    }

    private SysOperLog buildOperLog(AuditContext ctx) {
        SysOperLog log = new SysOperLog();
        log.setRequestId(ctx.getRequestId());
        log.setModule(ctx.getModule());
        log.setBusinessType(ctx.getBusinessType());
        log.setMethod(ctx.getMethod());
        log.setContent(ctx.getContent());
        log.setBusinessId(ctx.getBusinessId());
        log.setOperationType(ctx.getOperationType());
        log.setUserId(ctx.getUserId());
        log.setOperName(ctx.getOperName());
        log.setDeptName(ctx.getDeptName());
        log.setOperUrl(ctx.getOperUrl());
        log.setOperIp(ctx.getOperIp());
        log.setOperLocation(ctx.getOperLocation());
        log.setOperParam(ctx.getOperParam());
        log.setJsonResult(ctx.getJsonResult());
        log.setOperTime(ctx.getOperTime());
        return log;
    }
}
