package com.yunzhupaas.exception;

import cn.dev33.satoken.exception.SameTokenInvalidException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.alibaba.fastjson.JSON;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.ActionResultCode;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.config.ConfigValueUtil;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.database.util.NotTenantPluginHolder;
import com.yunzhupaas.database.util.TenantDataSourceUtil;
import com.yunzhupaas.entity.LogEntity;
import com.yunzhupaas.service.LogService;
import com.yunzhupaas.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 云筑产品开发平台组
 * @version V3.1.0
 * @copyright 深圳市乐程软件有限公司
 * @date 2024/3/16 10:10
 */
@Slf4j
@Controller
@ControllerAdvice
public class ResultException extends BasicErrorController {

    @Autowired
    private LogService logService;
    @Autowired
    private ConfigValueUtil configValueUtil;

    public ResultException(){
        super(new DefaultErrorAttributes(), new ErrorProperties());
    }

    @ResponseBody
    @ExceptionHandler(value = LoginException.class)
    public ActionResult loginException(LoginException e) {
        ActionResult result = ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());
        result.setData(e.getData());
        return result;
    }

    /**
     * 简单异常处理
     */
    @ResponseBody
    @ExceptionHandler(value = {ImportException.class, DataException.class, EncryptFailException.class})
    public ActionResult simpleException(Exception e) {
        ActionResult result = ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());
        return result;
    }

//    @ResponseBody
//    @ExceptionHandler(value = FileNotException.class)
//    public ActionResult loginException(FileNotException e) {
//        ActionResult result = ActionResult.fail(ActionResultCode.Fail.getCode(), "文件不存在");
//        return result;
//    }

    /**
     * 租户数据库异常
     *
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = {TenantDatabaseException.class, TenantInvalidException.class})
    public ActionResult<String> tenantDatabaseException(TenantInvalidException e) {
        String msg;
        if(e.getMessage() == null){
            if (configValueUtil.getMultiTenancyUrl().contains("https")) {
                // https 官网提示
                msg = MsgCode.LOG109.get();
            } else {
                msg = MsgCode.LOG110.get();
            }
        }else{
            msg = e.getMessage();
        }
        if(e.getLogMsg() != null){
            log.error(e.getLogMsg());
        }
        return ActionResult.fail(ActionResultCode.Fail.getCode(), msg);
    }



///
//    @ResponseBody
//    @ExceptionHandler(value = SQLSyntaxErrorException.class)
//    public ActionResult sqlException(SQLSyntaxErrorException e) {
//        ActionResult result;
//        log.error(e.getMessage());
//        e.printStackTrace();
//        if (e.getMessage().contains("Unknown database")) {
//            printLog(e, "请求失败");
//            result = ActionResult.fail(ActionResultCode.Fail.getCode(), "请求失败");
//        } else {
//            printLog(e, "数据库异常");
//            result = ActionResult.fail(ActionResultCode.Fail.getCode(), "数据库异常");
//        }
//        return result;
//    }
//
//    @ResponseBody
//    @ExceptionHandler(value = SQLServerException.class)
//    public ActionResult sqlServerException(SQLServerException e) {
//        ActionResult result;
//        printLog(e, "系统异常");
//        if (e.getMessage().contains("将截断字符串")) {
//            printLog(e, "某个字段字符长度超过限制，请检查。");
//            result = ActionResult.fail(ActionResultCode.Fail.getCode(), "某个字段字符长度超过限制，请检查。");
//        } else {
//            log.error(e.getMessage());
//            printLog(e, "数据库异常，请检查。");
//            result = ActionResult.fail(ActionResultCode.Fail.getCode(), "数据库异常，请检查。");
//        }
//        return result;
//    }

    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ActionResult methodArgumentNotValidException(MethodArgumentNotValidException e) {
        Map<String, String> map = new HashMap<>(16);
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        for (int i = 0; i < allErrors.size(); i++) {
            String s = allErrors.get(i).getCodes()[0];
            //用分割的方法得到字段名
            String[] parts = s.split("\\.");
            String part1 = parts[parts.length - 1];
            map.put(part1, allErrors.get(i).getDefaultMessage());
        }
        String json = JSON.toJSONString(map);
        ActionResult result = ActionResult.fail(ActionResultCode.ValidateError.getCode(), json);
        printLog(e, "字段验证异常", 4);
        return result;
    }

    @ResponseBody
    @ExceptionHandler(value = WorkFlowException.class)
    public ActionResult workFlowException(WorkFlowException e) {
        if (e.getCode() == 200) {
            Map<String, Object> map = JsonUtil.stringToMap(e.getMessage());
            return ActionResult.success(map);
        } else {
            if(e.getSuppressed()!=null) {
                printLog(e, "系统异常", 4);
            }
            return ActionResult.fail(e.getMessage());
        }
    }

    @ResponseBody
    @ExceptionHandler(value = WxErrorException.class)
    public ActionResult wxErrorException(WxErrorException e) {
        return ActionResult.fail(e.getError().getErrorCode(), MsgCode.AD103.get());
    }

    @ResponseBody
    @ExceptionHandler(value = ServletException.class)
    public void exception(ServletException e) throws Exception {
        log.error("系统异常:" + e.getMessage(), e);
        printLog(e, "系统异常", 4);
        throw new Exception();
    }

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ActionResult exception(Exception e) {
        log.error("系统异常:" + e.getMessage(), e);
        printLog(e, "系统异常", 4);
        if(e instanceof ConnectDatabaseException || e.getCause() instanceof ConnectDatabaseException){
            Throwable t = e;
            if(e.getCause() instanceof ConnectDatabaseException){
                t = e.getCause();
            }
            return ActionResult.fail(ActionResultCode.Fail.getCode(), t.getMessage());
        }
        return ActionResult.fail(ActionResultCode.Fail.getCode(), MsgCode.AD102.get());
    }

    /**
     * 权限码异常
     */
    @ResponseBody
    @ExceptionHandler(NotPermissionException.class)
    public ActionResult<Void> handleNotPermissionException(NotPermissionException e) {
        return ActionResult.fail(ActionResultCode.Fail.getCode(),  MsgCode.AD104.get());
    }

    /**
     * 角色权限异常
     */
    @ResponseBody
    @ExceptionHandler(NotRoleException.class)
    public ActionResult<Void> handleNotRoleException(NotRoleException e) {
        return ActionResult.fail(ActionResultCode.ValidateError.getCode(), MsgCode.AD104.get());
    }

    /**
     * 认证失败
     */
    @ResponseBody
    @ExceptionHandler(NotLoginException.class)
    public ActionResult<Void> handleNotLoginException(NotLoginException e) {
        return ActionResult.fail(ActionResultCode.SessionOverdue.getCode(), MsgCode.AD105.get());
    }

    /**
     * 无效认证
     */
    @ResponseBody
    @ExceptionHandler(SameTokenInvalidException.class)
    public ActionResult<Void> handleIdTokenInvalidException(SameTokenInvalidException e) {
        return ActionResult.fail(ActionResultCode.SessionOverdue.getCode(), MsgCode.AD106.get());
    }

    private void printLog(Exception e, String msg, int type) {
        try {
            UserInfo userInfo = UserProvider.getUser();
            if (userInfo.getId() == null) {
                e.printStackTrace();
                return;
            }
            //接口错误将不会进入数据库切源拦截器需要手动设置
            if (configValueUtil.isMultiTenancy() && TenantHolder.getDatasourceId() == null) {
                try {
                    TenantDataSourceUtil.switchTenant(userInfo.getTenantId());
                } catch (Exception ee){
                    e.printStackTrace();
                    return;
                }
            }
            LogEntity entity = new LogEntity();
            entity.setId(RandomUtil.uuId());
            entity.setUserId(userInfo.getUserId());
            entity.setUserName(userInfo.getUserName() + "/" + userInfo.getUserAccount());
//            if (!ServletUtil.getIsMobileDevice()) {
                entity.setDescription(msg);
//            }
            StringBuilder sb = new StringBuilder();
            sb.append(e.toString() + "\n");
            StackTraceElement[] stackArray = e.getStackTrace();
            for (int i = 0; i < stackArray.length; i++) {
                StackTraceElement element = stackArray[i];
                sb.append(element.toString() + "\n");
            }
            entity.setJsons(sb.toString());
            entity.setRequestUrl(ServletUtil.getRequest().getServletPath());
            entity.setRequestMethod(ServletUtil.getRequest().getMethod());
            entity.setType(type);
            entity.setUserId(userInfo.getUserId());
            // ip
            String ipAddr = IpUtil.getIpAddr();
            entity.setIpAddress(ipAddr);
            entity.setIpAddressName(IpUtil.getIpCity(ipAddr));
            entity.setCreatorTime(new Date());
            UserAgent userAgent = UserAgentUtil.parse(ServletUtil.getUserAgent());
            if (userAgent != null) {
                entity.setPlatForm(userAgent.getPlatform().getName() + " " + userAgent.getOsVersion());
                entity.setBrowser(userAgent.getBrowser().getName() + " " + userAgent.getVersion());
            }
            if (configValueUtil.isMultiTenancy() && StringUtil.isEmpty(TenantHolder.getDatasourceId())) {
                log.error("请求异常， 无登陆租户：" + ReflectionUtil.toString(entity), e);
            } else {
                logService.save(entity);
            }
        }catch (Exception g){
            log.error(g.getMessage());
        }finally {
            UserProvider.clearLocalUser();
            TenantProvider.clearBaseSystemIfo();
            TenantDataSourceUtil.clearLocalTenantInfo();
            NotTenantPluginHolder.clearNotSwitchFlag();
        }
    }


    /**
     * 覆盖默认的JSON响应
     */
    @Override
    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);

        if (status == HttpStatus.NOT_FOUND) {
            return new ResponseEntity<>(status);
        }
        return super.error(request);
    }

}

