package org.dromara.member.controller.app;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.member.domain.bo.AppOperLogBo;
import org.dromara.member.domain.vo.AppOperLogVo;
import org.dromara.member.service.AppOperLogService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;

/**
 * App端操作日志
 *
 * @author pengles
 */
@Tag(name = "App端操作日志管理", description = "App端操作日志管理相关接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/operLog")
public class AppOperLogController extends BaseController {

    private final AppOperLogService appOperLogService;

    /**
     * 查询App端操作日志列表
     */
    @Operation(summary = "查询操作日志列表", description = "查询App端操作日志列表")
    @SaCheckPermission("app:operLog:list")
    @GetMapping("/list")
    public TableDataInfo<AppOperLogVo> list(@Parameter(description = "查询条件") @Valid AppOperLogBo bo,
                                            @Parameter(description = "分页参数") @Valid PageQuery pageQuery) {
        return appOperLogService.queryPageList(bo, pageQuery);
    }

    /**
     * 获取当前用户的操作日志列表
     */
    @Operation(summary = "获取我的操作日志", description = "获取当前用户的操作日志列表")
    @SaCheckLogin
    @GetMapping("/my")
    public TableDataInfo<AppOperLogVo> getMyOperLogs(@Parameter(description = "查询条件") @Valid AppOperLogBo bo,
                                                     @Parameter(description = "分页参数") @Valid PageQuery pageQuery) {
        // 只查询当前用户的操作日志
        bo.setOperName(StpUtil.getLoginIdAsString());
        return appOperLogService.queryPageList(bo, pageQuery);
    }

    /**
     * 导出App端操作日志列表
     */
    @Operation(summary = "导出操作日志", description = "导出App端操作日志列表")
    @SaCheckPermission("app:operLog:export")
    @Log(title = "App端操作日志", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(@Parameter(description = "查询条件") @Valid AppOperLogBo bo, HttpServletResponse response) {
        List<AppOperLogVo> list = appOperLogService.queryList(bo);
        ExcelUtil.exportExcel(list, "App端操作日志", AppOperLogVo.class, response);
    }

    /**
     * 获取App端操作日志详细信息
     */
    @Operation(summary = "获取操作日志详情", description = "获取App端操作日志详细信息")
    @SaCheckPermission("app:operLog:query")
    @GetMapping("/{operId}")
    public R<AppOperLogVo> getInfo(@Parameter(description = "操作日志ID", required = true)
                                   @PathVariable @NotNull(message = "操作日志ID不能为空") @Positive(message = "操作日志ID必须为正数") Long operId) {
        return R.ok(appOperLogService.queryById(operId));
    }

    /**
     * 新增App端操作日志
     */
    @Operation(summary = "新增操作日志", description = "新增App端操作日志")
    @SaCheckPermission("app:operLog:add")
    @Log(title = "App端操作日志", businessType = BusinessType.INSERT)
    @PostMapping()
    public R<Void> add(@Parameter(description = "操作日志信息", required = true)
                       @Validated(AddGroup.class) @RequestBody @NotNull(message = "操作日志信息不能为空") AppOperLogBo bo) {
        return toAjax(appOperLogService.insertByBo(bo));
    }

    /**
     * 修改App端操作日志
     */
    @Operation(summary = "修改操作日志", description = "修改App端操作日志")
    @SaCheckPermission("app:operLog:edit")
    @Log(title = "App端操作日志", businessType = BusinessType.UPDATE)
    @PutMapping()
    public R<Void> edit(@Parameter(description = "操作日志信息", required = true)
                        @Validated(EditGroup.class) @RequestBody @NotNull(message = "操作日志信息不能为空") AppOperLogBo bo) {
        return toAjax(appOperLogService.updateByBo(bo));
    }

    /**
     * 删除App端操作日志
     */
    @Operation(summary = "删除操作日志", description = "删除App端操作日志")
    @SaCheckPermission("app:operLog:remove")
    @Log(title = "App端操作日志", businessType = BusinessType.DELETE)
    @DeleteMapping("/{operIds}")
    public R<Void> remove(@Parameter(description = "操作日志ID数组", required = true)
                          @PathVariable @NotEmpty(message = "操作日志ID不能为空") Long[] operIds) {
        return toAjax(appOperLogService.deleteWithValidByIds(List.of(operIds), true));
    }

    /**
     * 清空App端操作日志
     */
    @Operation(summary = "清空操作日志", description = "清空App端操作日志")
    @SaCheckPermission("app:operLog:remove")
    @Log(title = "App端操作日志", businessType = BusinessType.CLEAN)
    @DeleteMapping("/clean")
    public R<Void> clean() {
        appOperLogService.cleanOperLog();
        return R.ok();
    }

    /**
     * 根据时间范围删除操作日志
     */
    @Operation(summary = "按时间删除操作日志", description = "根据时间范围删除App端操作日志")
    @SaCheckPermission("app:operLog:remove")
    @Log(title = "App端操作日志", businessType = BusinessType.DELETE)
    @DeleteMapping("/cleanByTime")
    public R<Void> cleanByTime(@Parameter(description = "开始时间")
                               @RequestParam(required = false) LocalDateTime beginTime,
                               @Parameter(description = "结束时间")
                               @RequestParam(required = false) LocalDateTime endTime) {
        appOperLogService.deleteOperLogByTime(beginTime, endTime);
        return R.ok();
    }

    /**
     * 获取操作统计信息
     */
    @Operation(summary = "获取操作统计信息", description = "获取操作日志统计信息")
    @SaCheckLogin
    @GetMapping("/statistics")
    public R<Long> getStatistics(@Parameter(description = "操作人员名称")
                                 @RequestParam(required = false) @Size(max = 50, message = "操作人员名称长度不能超过50个字符") String operName,
                                 @Parameter(description = "开始时间")
                                 @RequestParam(required = false) LocalDateTime beginTime,
                                 @Parameter(description = "结束时间")
                                 @RequestParam(required = false) LocalDateTime endTime) {
        // 如果没有指定操作人，则查询当前用户的统计
        if (operName == null) {
            operName = StpUtil.getLoginIdAsString();
        }
        Long count = appOperLogService.getOperLogCount(operName, beginTime, endTime);
        return R.ok(count);
    }

    /**
     * 获取用户行为分析数据
     */
    @Operation(summary = "获取用户行为分析", description = "获取用户行为分析数据")
    @SaCheckLogin
    @GetMapping("/behavior")
    public R<List<AppOperLogVo>> getBehaviorAnalysis(@Parameter(description = "操作人员名称")
                                                     @RequestParam(required = false) @Size(max = 50, message = "操作人员名称长度不能超过50个字符") String operName,
                                                     @Parameter(description = "分析天数")
                                                     @RequestParam(defaultValue = "7") @Min(value = 1, message = "天数必须大于0") @Max(value = 365, message = "天数不能超过365天") Integer days) {
        // 如果没有指定操作人，则查询当前用户的行为分析
        if (operName == null) {
            operName = StpUtil.getLoginIdAsString();
        }
        List<AppOperLogVo> result = appOperLogService.getUserBehaviorAnalysis(operName, days);
        return R.ok(result);
    }

    /**
     * 获取热门操作统计
     */
    @Operation(summary = "获取热门操作统计", description = "获取热门操作统计")
    @SaCheckPermission("app:operLog:list")
    @GetMapping("/popular")
    public R<List<AppOperLogVo>> getPopularOperations(@Parameter(description = "统计天数")
                                                      @RequestParam(defaultValue = "7") @Min(value = 1, message = "天数必须大于0") @Max(value = 365, message = "天数不能超过365天") Integer days,
                                                      @Parameter(description = "返回数量限制")
                                                      @RequestParam(defaultValue = "10") @Min(value = 1, message = "限制数量必须大于0") @Max(value = 100, message = "限制数量不能超过100") Integer limit) {
        List<AppOperLogVo> result = appOperLogService.getPopularOperations(days, limit);
        return R.ok(result);
    }

    /**
     * 获取错误操作统计
     */
    @Operation(summary = "获取错误操作统计", description = "获取错误操作统计")
    @SaCheckPermission("app:operLog:list")
    @GetMapping("/errors")
    public R<List<AppOperLogVo>> getErrorOperations(@Parameter(description = "统计天数")
                                                    @RequestParam(defaultValue = "7") @Min(value = 1, message = "天数必须大于0") @Max(value = 365, message = "天数不能超过365天") Integer days) {
        List<AppOperLogVo> result = appOperLogService.getErrorOperations(days);
        return R.ok(result);
    }

    /**
     * 记录用户操作日志（供App端调用）
     */
    @Operation(summary = "记录操作日志", description = "记录用户操作日志（供App端调用）")
    @SaCheckLogin
    @Log(title = "App端操作日志", businessType = BusinessType.INSERT)
    @PostMapping("/record")
    public R<Void> recordOperation(@Parameter(description = "操作日志信息", required = true)
                                   @RequestBody @NotNull(message = "操作日志信息不能为空") @Valid AppOperLogBo bo) {
        // 设置操作人为当前登录用户
        bo.setOperName(StpUtil.getLoginIdAsString());
        bo.setOperTime(LocalDateTime.now());

        // 异步记录操作日志
        appOperLogService.asyncRecordOperLog(bo);
        return R.ok();
    }

    /**
     * 批量记录操作日志
     */
    @Operation(summary = "批量记录操作日志", description = "批量记录操作日志")
    @SaCheckLogin
    @Log(title = "App端操作日志", businessType = BusinessType.INSERT)
    @PostMapping("/batchRecord")
    public R<Void> batchRecordOperations(@Parameter(description = "操作日志列表", required = true)
                                         @RequestBody @NotEmpty(message = "操作日志列表不能为空") @Valid List<AppOperLogBo> operLogs) {
        // 设置操作人为当前登录用户
        String currentUser = StpUtil.getLoginIdAsString();
        operLogs.forEach(bo -> {
            bo.setOperName(currentUser);
            bo.setOperTime(LocalDateTime.now());
        });

        appOperLogService.batchInsertOperLog(operLogs);
        return R.ok();
    }

}