package cn.iocoder.yudao.module.system.controller.admin.worklog;

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserRespVO;
import cn.iocoder.yudao.module.system.controller.admin.worklog.vo.loginstance.*;
import cn.iocoder.yudao.module.system.convert.worklog.LogInstanceConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.permission.RoleService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.worklog.LogInstanceService;
import cn.iocoder.yudao.module.system.service.worklog.LogReadService;
import cn.iocoder.yudao.module.system.service.worklog.LogUseService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

@Tag(name = "管理后台 - 日志实例")
@RestController
@RequestMapping("/system/log-instance")
@Validated
public class LogInstanceController {

    @Resource
    private LogInstanceService logInstanceService;

    @Resource
    private LogReadService logReadService;

    @Resource
    private AdminUserService adminUserService;

    @Resource
    private RoleService roleService;

    @Resource
    private PermissionService permissionService;

    @Resource
    private LogUseService logUseService;

    @Resource
    private DeptService deptService;

    @PostMapping("/create")
    @Operation(summary = "创建日志实例")
    @PreAuthorize("@ss.hasPermission('system:log-instance:create')")
    public CommonResult<Long> createLogInstance(@Valid @RequestBody LogInstanceSaveReqVO createReqVO) {
        return success(logInstanceService.createLogInstance(createReqVO));
    }

    @PutMapping("/update")
    @Operation(summary = "更新日志实例")
    @PreAuthorize("@ss.hasPermission('system:log-instance:update')")
    public CommonResult<Boolean> updateLogInstance(@Valid @RequestBody LogInstanceSaveReqVO updateReqVO) {
        logInstanceService.updateLogInstance(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除日志实例")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('system:log-instance:delete')")
    public CommonResult<Boolean> deleteLogInstance(@RequestParam("id") Long id) {
        logInstanceService.deleteLogInstance(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得日志实例")
    @Parameter(name = "id", description = "编号", required = true, example = "1")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<LogInstanceRespVO> getLogInstance(@RequestParam("id") Long id) {

        //查看日志详情时，变更当前用户日志的阅读状态
        logReadService.updateReadStatus(id, getLoginUserId());

        //获取日志详情
        LogInstanceRespVO logInstance = logInstanceService.getLogInstance(id);

        //用户日志发起人详情
        AdminUserDO userDO = adminUserService.getUser(logInstance.getStartUserId());
        //设置日志发起人名称
        logInstance.setStartUserName(userDO.getNickname());
        //设置日志发起人头像
        logInstance.setAvatar(userDO.getAvatar());

        return success(logInstance);
    }

    @PostMapping("/getNextOrUp")
    @Operation(summary = "获取上一篇下一篇日志")
    public CommonResult<LogInstanceNextOrUpVO> getNextOrUp(@RequestBody LogInstancePageReqVO dto,
                                                           @Parameter(description = "当前id") @RequestParam("id") Long id,
                                                           @Parameter(description = "日志tab 0我收到的 1我发出的") @RequestParam("pagingType") Integer pagingType) {
        LogInstanceNextOrUpVO vo = logInstanceService.getNextOrUp(dto, id, pagingType);
        return success(vo);
    }

    @GetMapping("/page")
    @Operation(summary = "获得日志分页列表")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<IPage<LogInstanceRespVO>> getLogInstancePage(@ModelAttribute LogInstancePageReqVO pageReqVO) {

        IPage<LogInstanceRespVO> pageResult = logInstanceService.getLogInstancePage(pageReqVO, 0);
        return success(pageResult);
    }

    @GetMapping("/my-page")
    @Operation(summary = "获得我的日志分页列表")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<IPage<LogInstanceRespVO>> getMyLogInstancePage(@ModelAttribute LogInstancePageReqVO pageReqVO) {

        IPage<LogInstanceRespVO> pageResult = logInstanceService.getLogInstancePage(pageReqVO, 1);
        return success(pageResult);
    }

    @PostMapping("/get-dept")
    @Operation(summary = "获得我收到的日志页面中的部门列表")
    @Parameter(name = "companyDeptId", description = "公司部门编号")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<List<DeptRespVO>> getReadDept(@RequestParam(value = "companyDeptId", required = false) Long companyDeptId) {

        // 获取公司下级所有部门
        List<Long> companyDeptIds = convertList(deptService.getChildDept(companyDeptId), DeptDO::getId);

        // 获得用户角色
        List<RoleDO> userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(getLoginUserId()));
        List<String> codes = convertList(userRoles, RoleDO::getCode);
        if (codes.contains("super_admin")) {

           List<Long> deptIds = logUseService.getUseDeptList();
           if (CollectionUtil.isNotEmpty(companyDeptIds)) {
               deptIds = deptIds.stream().filter(companyDeptIds::contains).collect(Collectors.toList());
           }
           List<DeptDO> deptDOS = deptService.getDeptList(deptIds);

           return success(BeanUtils.toBean(deptDOS, DeptRespVO.class));
        }else {

            List<DeptRespVO> deptInfo = logInstanceService.getDeptInfo(companyDeptIds);
            return success(deptInfo);
        }
    }

    @GetMapping("/get-company-dept")
    @Operation(summary = "获得我收到的日志页面中的公司列表")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<List<DeptRespVO>> getReadCompanyDept() {

        // 获得公司编号列表
        List<DeptDO> companyDeptDOs = deptService.getCompanyDept(2);

        // 获得用户角色
        List<RoleDO> userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(getLoginUserId()));
        List<String> codes = convertList(userRoles, RoleDO::getCode);

        List<DeptRespVO> deptInfo;
        if (codes.contains("super_admin")) {

            List<Long> deptIds = logUseService.getUseDeptList();
            deptInfo = BeanUtils.toBean(deptService.getDeptList(deptIds), DeptRespVO.class);
        }else {

            // 获取可查看日志的部门列表
            deptInfo = logInstanceService.getDeptInfo(null);
        }

        List<DeptRespVO> respVOS = new ArrayList<>();
        for (DeptDO companyDept : companyDeptDOs) {
            if (deptInfo.stream().anyMatch(dept -> dept.getFlag().contains(companyDept.getId().toString()))) {
                respVOS.add(BeanUtils.toBean(companyDept, DeptRespVO.class));
            }
        }

        return success(respVOS);
    }

    @GetMapping("/get-dept-unReadCount")
    @Operation(summary = "获得我收到的日志页面中的各部门列表以及未读数量")
    @Parameter(name = "companyDeptId", description = "公司部门编号", required = true)
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<List<LogInstanceDeptUnReadCountVO>> getDeptUnReadCount(@RequestParam("companyDeptId") Long companyDeptId) {

        // 获取公司下级所有部门
        List<Long> companyDeptIds = convertList(deptService.getChildDept(companyDeptId), DeptDO::getId);

        // 获得用户角色
        List<RoleDO> userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(getLoginUserId()));
        List<String> codes = convertList(userRoles, RoleDO::getCode);
        if (codes.contains("super_admin")) {

            List<Long> deptIds = logUseService.getUseDeptList();
            if (CollectionUtil.isNotEmpty(companyDeptIds)) {
                deptIds = deptIds.stream().filter(companyDeptIds::contains).collect(Collectors.toList());
            }
            List<DeptDO> deptDOS = deptService.getDeptList(deptIds);

            return success(deptDOS.stream()
                    .map(dept -> {
                        LogInstanceDeptUnReadCountVO vo = new LogInstanceDeptUnReadCountVO();
                        vo.setDeptId(dept.getId());
                        vo.setDeptName(dept.getName());
                        vo.setUnReadCount(0);
                        return vo;
                    })
                    .collect(Collectors.toList()));
        }else {

            List<LogInstanceDeptUnReadCountVO> respVOS = logReadService.getDeptUnReadCount(companyDeptIds);
            return success(respVOS);
        }
    }

    @GetMapping("/get-upLog")
    @Operation(summary = "获取上一篇日志，用于导入上一篇日志")
    @Parameter(name = "formId", description = "日志模板编号", required = true, example = "1")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<LogInstanceRespVO> getUpLog(@RequestParam("formId") Long formId) {

        LogInstanceRespVO logInstanceRespVO = LogInstanceConvert.INSTANCE.convert(logInstanceService.getUpLog(formId));
        logInstanceRespVO = logInstanceService.setContentFun(logInstanceRespVO);

        return success(logInstanceRespVO);
    }

    @PostMapping("/getByLogId")
    @Operation(summary = "获得可查看日志的用户详情")
    @Parameter(name = "logId", description = "日志编号", required = true, example = "10")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    @DataPermission(enable = false)
    public CommonResult<List<Map<String, Object>>> getUserByLogId(@RequestParam("logId") Long logId) {

        List<Map<String, Object>> readUserVO = new ArrayList<>();

        //获取可查看者用户列表
        Map<Integer, List<Long>> usrMap = logReadService.getUserId(logId);

        //获取未读用户详情
        List<UserRespVO> unReadUserInfo = BeanUtils.toBean(adminUserService.getUserList(usrMap.get(0)), UserRespVO.class);
        //获取已读用户详情
        List<UserRespVO> readUserInfo = BeanUtils.toBean(adminUserService.getUserList(usrMap.get(1)), UserRespVO.class);

        Map<String, Object> unReadMap = new HashMap<>();
        Map<String, Object> readMap = new HashMap<>();

        unReadMap.put("readStatus", 0);
        unReadMap.put("value", unReadUserInfo);

        readMap.put("readStatus", 1);
        readMap.put("value", readUserInfo);

        readUserVO.add(unReadMap);
        readUserVO.add(readMap);

        return success(readUserVO);
    }

    @GetMapping("/get-unRead")
    @Operation(summary = "获取当前登录用户可查看日志未读的数量")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<Long> unRead() {

        return success(logReadService.getUnRead(getLoginUserId()));
    }

    @PostMapping("/setRead")
    @Operation(summary = "一键已读功能")
    @PreAuthorize("@ss.hasPermission('system:view-log:query')")
    public CommonResult<Boolean> setRead() {

        logReadService.setRead(getLoginUserId());
        return success(true);
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出日志实例 Excel")
    @PreAuthorize("@ss.hasPermission('system:log-instance:export')")
    @OperateLog(type = EXPORT)
    public void exportLogInstanceExcel(@Valid LogExportVO reqVO,
                                       HttpServletResponse response) throws IOException {

        List<LogExportExcelVO> list = logInstanceService.getLogInstanceExport(reqVO);


        // 输出excel
        ExcelWriterBuilder writer = EasyExcel.write(response.getOutputStream(), LogExportExcelVO.class);

        List<OnceAbsoluteMergeStrategy> strategies = getMergeStrategy(list);
        strategies.forEach(writer::registerWriteHandler);

        writer.autoCloseStream(false); // 不要自动关闭，交给 Servlet 自己处理
        writer.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()); // 基于 column 长度，自动适配。最大 255 宽度
        writer.registerWriteHandler(new HorizontalCellStyleStrategy(getHeadStyle(), getContentStyle()));
        writer.registerConverter(new LongStringConverter()); // 避免 Long 类型丢失精度
        writer.excelType(ExcelTypeEnum.XLS);
        writer.sheet("数据").doWrite(list);

        // 设置 header 和 contentType。写在最后的原因是，避免报错时，响应 contentType 已经被修改了
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("日志实例的拓展.xls", StandardCharsets.UTF_8.name()));
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");


//        // 导出 Excel
//        ExcelUtils.write(response, "日志实例的拓展.xls", "数据", LogExportExcelVO.class,
//                list);
    }

    public static List<OnceAbsoluteMergeStrategy> getMergeStrategy(List<LogExportExcelVO> dataList) {
        List<OnceAbsoluteMergeStrategy> strategies = new ArrayList<>();
        List<Integer> mergeCols = Arrays.asList(0, 1); // 需要合并的列索引

        // 遍历数据，生成合并区间
        for (int i = 0; i < dataList.size(); i++) {
            if (i > 0 && dataList.get(i).getNickname().equals(dataList.get(i - 1).getNickname()) && dataList.get(i).getTime().equals(dataList.get(i - 1).getTime())) {
                continue;
            }
            int startRow = i + 1;
            while (i < dataList.size() - 1 && dataList.get(i).getNickname().equals(dataList.get(i + 1).getNickname()) && dataList.get(i).getTime().equals(dataList.get(i + 1).getTime())) {
                i++;
            }
            for (int col : mergeCols) {
                strategies.add(new OnceAbsoluteMergeStrategy(startRow, i + 1, col, col));
            }
        }
        return strategies;
    }

    // 表头样式（居中）
    public static WriteCellStyle getHeadStyle() {
        WriteCellStyle headStyle = new WriteCellStyle();
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        return headStyle;
    }

    // 内容样式（居中）
    public static WriteCellStyle getContentStyle() {
        WriteCellStyle contentStyle = new WriteCellStyle();
        contentStyle.setWrapped(true);  // 启用自动换行
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        return contentStyle;
    }
}