package com.qmth.distributed.print.api;

import cn.hutool.core.codec.Base64Encoder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qmth.boot.api.annotation.Aac;
import com.qmth.boot.api.annotation.BOOL;
import com.qmth.boot.api.constant.ApiConstant;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.boot.core.enums.Platform;
import com.qmth.boot.tools.signature.SignatureType;
import com.qmth.distributed.print.business.bean.auth.AuthBean;
import com.qmth.distributed.print.business.bean.auth.ExpireTimeBean;
import com.qmth.distributed.print.business.bean.dto.MenuDto;
import com.qmth.distributed.print.business.bean.params.LoginParam;
import com.qmth.distributed.print.business.bean.result.EditResult;
import com.qmth.distributed.print.business.bean.result.LoginResult;
import com.qmth.distributed.print.business.config.DictionaryConfig;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.enums.DownloadFileEnum;
import com.qmth.distributed.print.business.enums.RoleTypeEnum;
import com.qmth.distributed.print.business.enums.UploadFileEnum;
import com.qmth.distributed.print.business.service.*;
import com.qmth.distributed.print.business.util.AuthUtil;
import com.qmth.distributed.print.business.util.RedisUtil;
import com.qmth.distributed.print.business.util.ServletUtil;
import com.qmth.distributed.print.business.util.security.Md5Utils;
import com.qmth.distributed.print.common.SignatureEntityTest;
import com.qmth.distributed.print.common.contant.SystemConstant;
import com.qmth.distributed.print.common.enums.ExceptionResultEnum;
import com.qmth.distributed.print.common.util.Result;
import com.qmth.distributed.print.common.util.ResultUtil;
import com.qmth.distributed.print.common.util.SessionUtil;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Date: 2021/3/30.
 */
@Api(tags = "系统Controller")
@RestController
@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/${prefix.url.common}")
@Aac(strict = BOOL.TRUE, platform = Platform.WEB)
public class SysController {
    private final static Logger log = LoggerFactory.getLogger(SysController.class);

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private BasicVerifyCodeService basicVerifyCodeService;

    @Autowired
    private DictionaryConfig dictionaryConfig;

    @Resource
    CacheService cacheService;

    @Resource
    TBSessionService tbSessionService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    CommonService commonService;

    @Resource
    TBTaskService tbTaskService;

    @Resource
    BasicAttachmentService basicAttachmentService;

    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private SysUserRoleService sysUserRoleService;

    /**
     * 登录
     *
     * @param login
     * @return
     */
    @ApiOperation(value = "登录")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "用户信息", response = LoginResult.class)})
    @Aac(auth = BOOL.FALSE)
    public Result login(@ApiParam(value = "用户信息", required = true) @Valid @RequestBody LoginParam login, BindingResult bindingResult) throws NoSuchAlgorithmException {
        if (bindingResult.hasErrors()) {
            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
        }

        BasicSchool basicSchool = null;
        if (!Objects.equals(login.getSchoolCode().toUpperCase(), RoleTypeEnum.ADMIN.name())) {
            basicSchool = cacheService.schoolCache(login.getSchoolCode());
            if (Objects.isNull(basicSchool)) {
                throw ExceptionResultEnum.SCHOOL_NO_DATA.exception();
            }
            if (Objects.nonNull(basicSchool.getEnable()) && !basicSchool.getEnable()) {
                throw ExceptionResultEnum.SCHOOL_ENABLE.exception();
            }
        }
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getLoginName, login.getLoginName());
        if (!Objects.equals(login.getSchoolCode().toUpperCase(), RoleTypeEnum.ADMIN.name())) {
            wrapper.lambda().eq(SysUser::getSchoolId, basicSchool.getId());
        }
        List<SysUser> userList = sysUserService.list(wrapper);
        //用户不存在
        if (Objects.isNull(userList) || userList.size() == 0) {
            throw ExceptionResultEnum.USER_NO_DATA.exception();
        }
        if (Objects.equals(login.getSchoolCode().toUpperCase(), RoleTypeEnum.ADMIN.name())) {
            userList.forEach(o -> {
                AuthBean authBean = commonService.getUserAuth(o.getId());
                if (Objects.nonNull(authBean) && Objects.nonNull(authBean.getRoleList()) && authBean.getRoleList().size() > 0) {
                    Set<RoleTypeEnum> roleType = authBean.getRoleList().stream().map(s -> s.getType()).collect(Collectors.toSet());
                    if (!roleType.contains(RoleTypeEnum.ADMIN) && !roleType.contains(RoleTypeEnum.CUSTOMER)) {
                        throw ExceptionResultEnum.ERROR.exception("学校代码为admin只允许超级管理员登录");
                    }
                }
            });
        }
        if (userList.size() > 1) {
            throw ExceptionResultEnum.ERROR.exception("查询的用户有多条");
        }
        SysUser sysUser = userList.get(0);
        if (Objects.nonNull(sysUser.getSchoolId()) && sysUser.getSchoolId().longValue() != basicSchool.getId().longValue()) {
            throw ExceptionResultEnum.ERROR.exception("用户学校不匹配");
        }
        //密码不正确
        if (!Objects.equals(login.getPassword(), sysUser.getPassword())) {
            throw ExceptionResultEnum.PASSWORD_ERROR.exception();
        }
        //停用
        if (!sysUser.getEnable()) {
            throw ExceptionResultEnum.USER_ENABLE.exception();
        }
//        校验验证码
        SysConfig value = sysConfigService.getByKey("sys.code.enable");
        if (Objects.nonNull(value) && value.getConfigValue().equals("true")) {
            String code = login.getCode();
            if (Objects.isNull(code)) {
                throw ExceptionResultEnum.ERROR.exception("验证码为空");
            }
            if (!dictionaryConfig.smsDomain().getSmsNormalCode().equals(code)) {
                QueryWrapper<BasicVerifyCode> codeWrapper = new QueryWrapper<>();
                codeWrapper.lambda().eq(BasicVerifyCode::getMobileNumber, sysUser.getMobileNumber()).eq(BasicVerifyCode::getUserId, sysUser.getId());
                BasicVerifyCode accessControl = basicVerifyCodeService.getOne(codeWrapper);
                if (accessControl == null || (accessControl != null && !accessControl.getVerifyCode().equals(code))) {
                    throw ExceptionResultEnum.ERROR.exception("短信验证码错误,请仔细核对后再次输入");
                }

                if (new Date(accessControl.getExpireTime()).before(new Date())) {
                    throw ExceptionResultEnum.ERROR.exception("短信验证码已过期");
                }
            }
        }

        Platform platform = ServletUtil.getRequestPlatform();
        String deviceId = ServletUtil.getRequestDeviceId();
        //添加用户鉴权缓存
        AuthBean authBean = cacheService.userAuthCache(sysUser.getId());
        if (Objects.isNull(authBean)) {
            throw ExceptionResultEnum.ROLE_ENABLE_AUTHORIZATION.exception();
        }
        //生成token
        String token = SystemConstant.getUuid();
        cacheService.userCache(sysUser.getId());
        //添加用户会话缓存
        Set<RoleTypeEnum> roleType = authBean.getRoleList().stream().map(s -> s.getType()).collect(Collectors.toSet());
        String sessionId = SessionUtil.digest(sysUser.getId(), Math.abs(roleType.toString().hashCode()), platform.name());
        //TODO 测试用
        String test = SignatureEntityTest.build(SignatureType.TOKEN, sessionId, token);
        ExpireTimeBean expireTime = AuthUtil.getExpireTime(platform);
        TBSession tbSession = new TBSession(sessionId, String.valueOf(sysUser.getId()), roleType.toString(),
                platform.name(), platform.name(), deviceId, ServletUtil.getRequest().getLocalAddr(), token,
                expireTime.getDate().getTime());
        tbSessionService.saveOrUpdate(tbSession);
        redisUtil.setUserSession(sessionId, tbSession, expireTime.getExpireSeconds());

        LoginResult loginResult = new LoginResult(sysUser, sessionId, test, roleType);
        loginResult.setSchoolInfo(Objects.nonNull(authBean.getSchool()) ? loginResult.new SchoolNativeBean(authBean.getSchool()) : null);
        loginResult.setOrgInfo(Objects.nonNull(authBean.getOrg()) ? loginResult.new OrgNativeBean(authBean.getOrg()) : null);
        return ResultUtil.ok(loginResult);
    }

    /**
     * 登出
     *
     * @return
     */
    @ApiOperation(value = "登出")
    @RequestMapping(value = "/logout", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "返回信息", response = EditResult.class)})
    public Result logout() {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
        AuthBean authBean = cacheService.userAuthCache(sysUser.getId());
        if (Objects.isNull(authBean)) {
            throw ExceptionResultEnum.NOT_LOGIN.exception();
        }
        tbSessionService.removeById(tbSession.getId());
        redisUtil.deleteUserSession(tbSession.getId());
        cacheService.removeUserCache(sysUser.getId());
        cacheService.removeUserAuthCache(sysUser.getId());
        return ResultUtil.ok(new EditResult(sysUser.getId()));
    }

    /**
     * 发送验证码
     *
     * @param loginParam
     * @return
     */
    @ApiOperation(value = "发送验证码")
    @RequestMapping(value = "/getVerifyCode", method = RequestMethod.POST)
    public Object getverifyCode(@RequestBody LoginParam loginParam) {
        String loginName = loginParam.getLoginName();
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getLoginName, loginName);
        SysUser user = sysUserService.getOne(wrapper);
        //用户不存在
        if (Objects.isNull(user)) {
            throw ExceptionResultEnum.ERROR.exception("用户不存在");
        }
        String password = Md5Utils.toMd5Hex(loginParam.getPassword());
        if (password.equals(user.getPassword())) {
            throw ExceptionResultEnum.ERROR.exception("密码错误");
        }
        String mobileNumber = user.getMobileNumber();
        if (Objects.isNull(mobileNumber)) {
            throw ExceptionResultEnum.ERROR.exception("用户未绑定手机号码");
        }
        basicVerifyCodeService.sendVeirfyCode(mobileNumber, user.getId());
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "根据机构代码查询机构信息接口")
    @RequestMapping(value = "/school/query_by_school_code", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "学校信息", response = EditResult.class)})
    @Aac(auth = BOOL.FALSE)
    public Result queryBySchoolCode(@ApiParam(value = "机构code", required = true) @RequestParam String code) {
        if (!Objects.equals(code.toUpperCase(), RoleTypeEnum.ADMIN.name())) {
            BasicSchool basicSchool = cacheService.schoolCache(code);
            if (Objects.isNull(basicSchool)) {
                throw ExceptionResultEnum.SCHOOL_NO_DATA.exception();
            }
            return ResultUtil.ok(Collections.singletonMap(SystemConstant.LOGO, basicSchool.getLogo()));
        } else {
            return ResultUtil.ok(Collections.singletonMap(SystemConstant.LOGO, dictionaryConfig.sysDomain().getAdminLogoUrl()));
        }
    }

    @ApiOperation(value = "文件上传接口")
    @RequestMapping(value = "/file/upload", method = RequestMethod.POST)
    @Transactional
    @ApiResponses({@ApiResponse(code = 200, message = "返回信息", response = EditResult.class)})
    public Result fileUpload(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file,
                             @ApiParam(value = "上传文件类型", required = true) @RequestParam UploadFileEnum type) {
        BasicAttachment basicAttachment = null;
        try {
            basicAttachment = basicAttachmentService.saveAttachment(file, ServletUtil.getRequestMd5(), type);
            if (Objects.isNull(basicAttachment)) {
                throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
            }
        } catch (Exception e) {
            log.error("请求出错", e);
            if (Objects.nonNull(basicAttachment)) {
                basicAttachmentService.deleteAttachment(basicAttachment);
            }
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }

        return ResultUtil.ok(new EditResult(basicAttachment.getId(), commonService.filePreview(basicAttachment.getPath()), basicAttachment.getPages()));
    }

    @ApiOperation(value = "文件下载接口")
    @RequestMapping(value = "/file/download", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "返回信息", response = EditResult.class)})
    public Result fileDownload(@ApiParam(value = "任务id", required = true) @RequestParam String id,
                               @ApiParam(value = "下载文件类型", required = true) @RequestParam DownloadFileEnum type) {
        TBTask tbTask = tbTaskService.getById(Long.parseLong(id));
        if (Objects.isNull(tbTask)) {
            throw ExceptionResultEnum.TASK_NO_DATA.exception();
        }
        String path = null;
        switch (type.ordinal()) {
            case 0:
                path = tbTask.getImportFilePath();
                break;
            case 1:
                path = tbTask.getReportFilePath();
                break;
            default:
                path = tbTask.getResultFilePath();
                break;
        }
        if (Objects.isNull(path)) {
            throw ExceptionResultEnum.PATH_NO_DATA.exception();
        }
        return ResultUtil.ok(new EditResult(commonService.filePreview(path)));
    }

    @ApiOperation(value = "文件预览接口")
    @RequestMapping(value = "/file/preview", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "返回信息", response = EditResult.class)})
    public Result filePreview(@ApiParam(value = "附件id", required = true) @RequestParam String id) {
        BasicAttachment basicAttachment = basicAttachmentService.getById(Long.parseLong(id));
        return ResultUtil.ok(new EditResult(commonService.filePreview(basicAttachment.getPath())));
    }

    @ApiOperation(value = "查询用户权限")
    @RequestMapping(value = "/get_menu", method = RequestMethod.POST)
    public Result getMenu() {
        List<MenuDto> list = sysUserRoleService.listByUserId();
        return ResultUtil.ok(list);
    }

}