package com.iformall.controller.sys;

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.iformall.common.ErrorCode;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.controller.base.BaseController;
import com.iformall.domain.po.*;
import com.iformall.domain.vo.MallUserInfoVo;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.annotation.SystemControllerLog;
import com.iformall.service.*;
import com.iformall.shiro.UserSession;
import com.iformall.shiro.UseriFormallToken;
import com.iformall.utils.ShiroUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RestController
@Api(description = "登录相关接口")
public class HomeController extends BaseController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${version}")
    private String version;

    @Autowired
    private Producer producer;

    @Autowired
    private MallUserInfoService mallUserInfoService;

    @Autowired
    private MallUserRoleService mallUserRoleService;

    @Autowired
    private WxAppinfoService appinfoService;

    @Autowired
    private WxMsgValidationcodeService wxMsgValidationcodeService;

    @Autowired
    private MallUserActionService mallUserActionService;

    @Autowired
    private WxMallService mallService;

    @ApiOperation("验证码")
    @GetMapping("/captcha.jpg")
    public void captcha(HttpServletResponse response)throws ServletException, IOException {
        logger.debug("[" + getIpAddr() + "] HomeController::captcha");
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");

        //生成文字验证码
        String text = producer.createText();
        //生成图片验证码
        BufferedImage image = producer.createImage(text);
        //保存到shiro session
        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);

        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image, "jpg", out);
        IOUtils.closeQuietly(out);
    }

    @ApiOperation("登录")
    @PostMapping("/doLogin")
    public ResultData login(@RequestBody MallUserInfo user, HttpServletResponse response) {
        String ipaddress = getIpAddr();
        logger.debug("[" + ipaddress + "] HomeController::doLogin");
        try {
            String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
            if(!user.getCaptcha().equalsIgnoreCase(kaptcha)){
                return new ResultData(ErrorCode.KAPCHA_NOT_EQUAL);
            }
        } catch (MallinkException e) {
            logger.error("验证码" + e.getMessage());
            return new ResultData(ErrorCode.KAPCHA_NOT_VALID.getCode(), e.getMessage());
        }


        ResultData data = new ResultData();
        if (StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())) {
//            throw new SystemException(ErrorCode.LOGIN_USER_OR_PWD_ERROR);
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL);
        }
        // check user
        MallUserInfo userCheck = mallUserInfoService.getByUsername(user.getUsername());
        if(userCheck == null) {
            logger.error(ErrorCode.USER_IS_EMPTY.getMessage());
            return new ResultData(ErrorCode.USER_IS_EMPTY);
        }
        if(userCheck.getStatus().equals(EnumMallUserStatus.NOT_VALID.getCode())) {
            logger.error(ErrorCode.USER_IS_LOCKED.getMessage());
            return new ResultData(ErrorCode.USER_IS_LOCKED);
        }
        boolean isLogin = false;
        try {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
            subject.login(token);
            isLogin = true;
        } catch (UnknownAccountException e) {
            logger.error(e.getMessage());
            return new ResultData(ErrorCode.USER_IS_EMPTY);
        } catch (DisabledAccountException e) {
            logger.error(e.getMessage());
            return new ResultData(ErrorCode.USER_IS_LOCKED);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ResultData(ErrorCode.USER_PASSWD_ERR);
        }

        if(isLogin) {
            MallUserInfo info = (MallUserInfo) SecurityUtils.getSubject().getSession().getAttribute(UserSession.userInfo);
            info.protectInfos();
            mallUserActionService.saveActionInfo(info.getTenantId(), EnumMallUserAction.CONTROLLER.getCode(), ipaddress, info.getId(), "用户登录");

            WxMall mall = mallService.getByTenantId(info.getTenantId());
            if(mall == null) {
                logger.error("未配置相应的mall");
                return new ResultData(Result.ERROR, "未配置相应的mall");
            }
            if(!mall.isValid()) {
                logger.error("mall未启用");
                return new ResultData(Result.ERROR, "mall未启用");
            }

            try {
                String cookieName = URLEncoder.encode(info.getUsername(), "utf-8");
                Cookie unameCookie = new Cookie("uname", cookieName);
                unameCookie.setPath("/");
                unameCookie.setMaxAge(3600);
                response.addCookie(unameCookie);

                Map map = new HashMap();
                map.put("username", info.getUsername());
                map.put("withWechat", info.getWithWechat());
                data.data = map;
            } catch (Exception e) {
                logger.error(e.getMessage());
                return new ResultData(Result.ERROR, e.getMessage());
            }
        }
        return data;
    }

    private boolean isInMobileAdmin(MallUserInfo user) {
        // 检查用户是否有 移动端数据塔台 权限
        boolean isInMobile = false;
        isInMobile = mallUserRoleService.checkIsMobileAdmin(user);
        return isInMobile;
    }

    @ApiOperation("B端登录")
    @PostMapping("/bHidLogin")
    public ResultData bLogin(@RequestBody MallUserInfo user, HttpServletResponse response) {
        String ipaddress = getIpAddr();
        logger.debug("[" + ipaddress + "] HomeController::bHidLogin");
        ResultData data = new ResultData();
        // tenantId is appId
        if (StringUtils.isBlank(user.getTenantId())) {
            logger.error("appid信息未提供");
            return new ResultData(ErrorCode.MALL_INFO_NOT_FOUND);
        }
        WxAppinfo appinfo = appinfoService.getByAppId(user.getTenantId());
        if (appinfo == null) {
            logger.error("appid未找到");
            return new ResultData(ErrorCode.MALL_INFO_NOT_FOUND);
        }
        if (StringUtils.isNotBlank(user.getPhone())) {
            // 领导登录
            user = mallUserInfoService.getByPhone(user.getPhone(), appinfo.getTenantId());
            if (user == null || !isInMobileAdmin(user)) {
                return new ResultData(ErrorCode.USER_NO_PERMISSION);
            }
        } else if (StringUtils.isNotBlank(user.getBopenId())) {
            // 领导登录
            user = mallUserInfoService.getByBOpenId(user.getBopenId(), appinfo.getTenantId());
            if (user == null || !isInMobileAdmin(user)) {
                return new ResultData(ErrorCode.USER_NO_PERMISSION);
            }
        } else {
            if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) {
//            throw new SystemException(ErrorCode.LOGIN_USER_OR_PWD_ERROR);
                return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL);
            }
        }

        boolean isLogin = false;
        try {
            Subject subject = SecurityUtils.getSubject();
            UseriFormallToken token = new UseriFormallToken(user.getUsername());
            subject.login(token);
            isLogin = true;
        } catch (UnknownAccountException e) {
            logger.error(e.getMessage());
            return new ResultData(ErrorCode.USER_IS_EMPTY);
        } catch (DisabledAccountException e) {
            logger.error(e.getMessage());
            return new ResultData(ErrorCode.USER_IS_LOCKED);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ResultData(ErrorCode.USER_PASSWD_ERR);
        }

        if (isLogin) {
            MallUserInfo info = (MallUserInfo) SecurityUtils.getSubject().getSession().getAttribute(UserSession.userInfo);
            info.protectInfos();

            mallUserActionService.saveActionInfo(info.getTenantId(), EnumMallUserAction.CONTROLLER.getCode(), ipaddress, info.getId(), "用户登录");

            WxMall mall = mallService.getByTenantId(info.getTenantId());
            if (mall == null) {
                logger.error("未配置相应的mall");
                return new ResultData(Result.ERROR, "未配置相应的mall");
            }
            if (!mall.isValid()) {
                logger.error("mall未启用");
                return new ResultData(Result.ERROR, "mall未启用");
            }
            try {
                String cookieName = URLEncoder.encode(info.getUsername(), "utf-8");
                Cookie unameCookie = new Cookie("uname", cookieName);
                unameCookie.setPath("/");
                unameCookie.setMaxAge(3600);
                response.addCookie(unameCookie);

                Map map = new HashMap();
                map.put("username", info.getUsername());
                map.put("withWechat", info.getWithWechat());
                data.data = map;

            } catch(Exception e){
                logger.error(e.getMessage());
                return new ResultData(ErrorCode.USER_PASSWD_ERR);
            }
        }
        return data;
    }

    @ApiOperation("发送手机验证码")
    @GetMapping("sendLoginPhoneCode")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true)})
    public ResultData sendLoginPhoneCode(String phone) {
        logger.debug("[" + getIpAddr() + "] HomeController::sendlogincode");
        // 1. 检查手机号是否在用户列表里, 是否只有一个
        // 2. 发送手机验证码, 直接发
        List<MallUserInfoVo> users = mallUserInfoService.getUserByPhone(phone);
        if(users.size() <= 0) {
            logger.error(ErrorCode.USER_IS_EMPTY.getMessage());
            return new ResultData(ErrorCode.USER_IS_EMPTY);
        }
        if(users.size() > 1) {
            logger.error(ErrorCode.USER_IS_MULTI.getMessage());
            return new ResultData(ErrorCode.USER_IS_MULTI);
        }
        MallUserInfoVo user = users.get(0);
        if (user==null) {
            logger.error("用户不存在, userName: " + user.getUsername());
            return new ResultData(ErrorCode.USER_IS_EMPTY);
        }
        if(user.getStatus() == EnumMallUserStatus.NOT_VALID.getCode()){
            logger.error("用户已停用, userName: " + user.getUsername());
            return new ResultData(ErrorCode.USER_IS_LOCKED);
        }
        WxMsgValidationcode wxMsgValidationcode = new WxMsgValidationcode();
        wxMsgValidationcode.setPhone(phone);
        wxMsgValidationcode.setTenantId(user.getTenantId());
        wxMsgValidationcode.setType(EnumMsgModel.VALIDATION_CODE.getCode());
        return wxMsgValidationcodeService.sendvalidationcode(wxMsgValidationcode);
    }

    @ApiOperation(value = "手机验证码登录", notes = "{\"phone\",\"string\",\"code\",\"string\"}")
    @PostMapping("/doLoginByPhone")
    public ResultData doLoginByPhone(@RequestBody Map<String, String> params, HttpServletResponse response) {
        String ipaddress = getIpAddr();
        logger.debug("[" + ipaddress + "] HomeController::doLoginByPhone");
        // String phone,String code,String pwd
        String phone = params.get("phone");
        String code = params.get("code");

        if (StringUtils.isBlank(phone)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "userName不能为空");
        }
        if (StringUtils.isBlank(code)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "验证码不能为空");
        }

        // 获取用户信息列表
        List<MallUserInfoVo> userList = mallUserInfoService.getUserByPhone(phone);
        if(userList.size() == 1) {
            MallUserInfoVo user = userList.get(0);
            // check 验证码正确
            boolean isValidCode = false;
            try {
                isValidCode =  mallUserInfoService.checkCodeValid(user, code);
            } catch (Exception e) {
                return new ResultData(Result.ERROR, e.getMessage());
            }
            if(isValidCode) {
                // 验证码正确，直接登录
                try {
                    Subject subject = SecurityUtils.getSubject();
                    UseriFormallToken token = new UseriFormallToken(user.getUsername());
                    subject.login(token);

                    MallUserInfo info = (MallUserInfo) SecurityUtils.getSubject().getSession().getAttribute(UserSession.userInfo);
                    info.protectInfos();
                    mallUserActionService.saveActionInfo(info.getTenantId(), EnumMallUserAction.CONTROLLER.getCode(), ipaddress, info.getId(), "用户手机号登录");

                    WxMall mall = mallService.getByTenantId(info.getTenantId());
                    if (mall == null) {
                        logger.error("未配置相应的mall");
                        return new ResultData(Result.ERROR, "未配置相应的mall");
                    }
                    if (!mall.isValid()) {
                        logger.error("mall未启用");
                        return new ResultData(Result.ERROR, "mall未启用");
                    }

                    String cookieName = URLEncoder.encode(info.getUsername(), "utf-8");
                    Cookie unameCookie = new Cookie("uname", cookieName);
                    unameCookie.setPath("/");
                    unameCookie.setMaxAge(3600);
                    response.addCookie(unameCookie);

                    Map map = new HashMap();
                    map.put("username", info.getUsername());
                    map.put("withWechat", info.getWithWechat());

                    return new ResultData(map);
                } catch (MallinkException e) {
                    return new ResultData(e.getErrorCode(), e.getMessage());
                } catch (Exception e) {
                    return new ResultData(ErrorCode.USER_PASSWD_ERR);
                }
            } else {
                return new ResultData(ErrorCode.MSG_VERIFY_CODE_NOT_FOUND);
            }
        } else {
            return new ResultData(ErrorCode.USER_IS_EMPTY);
        }
    }

    @ApiOperation("登出")
    @GetMapping("/logout")
    @SystemControllerLog(description = "用户登出")
    public ResultData logout() {
        logger.debug("[" + getIpAddr() + "] HomeController::logout");
        ResultData data = new ResultData();
        SecurityUtils.getSubject().logout();
        return data;
    }

    @ApiOperation("获取后端版本号")
    @GetMapping("/version")
    public ResultData version() {
        logger.debug("[" + getIpAddr() + "] HomeController::version");
        return new ResultData(version);
    }
}