package com.ycwl.basic.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.constant.PermissionConstant;
import com.ycwl.basic.constant.RequestConstant;
import com.ycwl.basic.exception.CheckTokenException;
import com.ycwl.basic.exception.MissTokenException;
import com.ycwl.basic.exception.PermissionException;
import com.ycwl.basic.exception.TokenExpireException;
import com.ycwl.basic.mapper.AdminUserMapper;
import com.ycwl.basic.mapper.ScenicAccountMapper;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.pc.adminUser.entity.LoginEntity;
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
import com.ycwl.basic.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;

import static com.ycwl.basic.constant.JwtRoleConstant.ADMIN;
import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;

@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Lazy
    @Autowired
    RedisTemplate redisTemplate;
    @Lazy
    @Autowired
    private ScenicAccountMapper scenicAccountMapper;
    @Lazy
    @Autowired
    private AdminUserMapper adminUserMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod handlerMethod)) {
            return true;
        }
        String requestURI = request.getRequestURI();

        // 获取类上面的注解
        IgnoreToken ignoreClassToken = handlerMethod.getBeanType().getAnnotation(IgnoreToken.class);
        // 获取方法上的注解
        IgnoreToken ignoreMethodToken = handlerMethod.getMethodAnnotation(IgnoreToken.class);
        if (ignoreClassToken != null || ignoreMethodToken != null) {
            // 放行
            return true;
        }

        /*
        验证token
         */
        // 获取 token
        String token = getToken(request);
        if (StringUtils.isEmpty(token)) {
            log.error("==>  请求 header 缺少 Token [{}], URL [{}]", token, request.getRequestURL());
            throw new MissTokenException("请求头缺少token");
        }


        // 解析 token  & 验证 token 有效期
        JwtInfo jwtInfo;
        try {
            jwtInfo = JwtTokenUtil.parsingToken(token);
            if (Strings.CS.equals(jwtInfo.getRoleId(), MERCHANT.type)) {
                Long merchantId = jwtInfo.getUserId();
                ScenicAccountEntity account = scenicAccountMapper.findAccountById(merchantId.toString());
                LocalDateTime expireTime = jwtInfo.getExpireTime();
                if (account.getUpdateTime().toInstant().getEpochSecond() != expireTime.atZone(ZoneId.systemDefault()).toEpochSecond()) {
                    throw new TokenExpireException("token过期");
                }
            }
            if (Strings.CS.equals(jwtInfo.getRoleId(), ADMIN.type)) {
                Long adminId = jwtInfo.getUserId();
                LoginEntity account = adminUserMapper.getById(adminId);
                LocalDateTime expireTime = jwtInfo.getExpireTime();
                if (account.getUpdateAt().toInstant().getEpochSecond() != expireTime.atZone(ZoneId.systemDefault()).toEpochSecond()) {
                    throw new TokenExpireException("token过期");
                }
            }
            BaseContextHandler.setToken(token);
            BaseContextHandler.setName(jwtInfo.getName());
            BaseContextHandler.setUserId(String.valueOf(jwtInfo.getUserId()));
            BaseContextHandler.setAccount(jwtInfo.getAccount());
            BaseContextHandler.setPhone(jwtInfo.getPhone());
            BaseContextHandler.setRoleId(jwtInfo.getRoleId());
            BaseContextHandler.setUserExpireTime(jwtInfo.getExpireTime());
        } catch (CheckTokenException e) {
            throw new CheckTokenException("token无效");
        }

//        if (getBusinessWhite(requestURI)) {
//            if (!getPermission(jwtInfo.getRoleId(), requestURI)) {
//                throw new PermissionException("无权访问");
//            }
//        }

        return true;
    }

    public static void sendJsonMessage(HttpServletResponse response, Object obj) {

        ObjectMapper objectMapper = new ObjectMapper();
        response.setContentType("application/json; charset=utf-8");

        try (PrintWriter writer = response.getWriter()) {
            writer.print(objectMapper.writeValueAsString(obj));
            response.flushBuffer();
        } catch (IOException e) {
            log.warn("响应json数据给前端异常 -> {}", e.getMessage(), e);
        }

    }


    /**
     * 在请求完全结束后调用, 常用于清理资源等工作
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        BaseContextHandler.remove();
    }

    public String getToken(HttpServletRequest request) {
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            if (request.getCookies() != null) {
                for (Cookie cookie : request.getCookies()) {
                    if (("token").equals(cookie.getName())) {
                        token = cookie.getValue();
                    }
                }
            }
        }
        return token;
    }

    /**
     * 业务接口白名单
     *
     * @param
     * @return
     */
    private boolean getBusinessWhite(String path) {
        if (path.contains(RequestConstant.UPDATE_PASSWORD)) {
            return false;
        }
        return true;
    }


    private boolean getPermission(String roleId, String url) {
        Object permissionUrl = redisTemplate.opsForValue().get(PermissionConstant.USER_PERMISSION_URL + roleId);
        if (permissionUrl != null) {
            List<String> permissionUrlList = (List<String>) permissionUrl;
            String urlWithParameters = url.substring(url.lastIndexOf("/") + 1, url.length());
            boolean flag = StringUtils.isNumeric(urlWithParameters);
            if (flag) {
                url = url.substring(0, url.lastIndexOf("/"));
            }
            for (String str : permissionUrlList) {
                if (str.equals(url)) {
                    return true;
                }

            }
        }
        return false;
    }

}
