import cn.reghao.bnt.web.WebApplication;
import cn.reghao.bnt.web.admin.controller.LoginController;
import cn.reghao.bnt.web.admin.db.repository.MenuRepository;
import cn.reghao.bnt.web.admin.db.repository.RoleRepository;
import cn.reghao.bnt.web.admin.db.repository.UserRepository;
import cn.reghao.bnt.web.admin.model.constant.MenuType;
import cn.reghao.bnt.web.admin.model.constant.RoleType;
import cn.reghao.bnt.web.admin.model.po.Menu;
import cn.reghao.bnt.web.admin.model.po.Role;
import cn.reghao.bnt.web.admin.model.po.User;
import cn.reghao.bnt.web.admin.service.AccountService;
import cn.reghao.bnt.web.admin.service.AccountSessionService;
import cn.reghao.bnt.web.admin.service.MenuService;
import cn.reghao.bnt.web.config.web.exception.ControllerErrorHandler;
import cn.reghao.jutil.jdk.security.RandomString;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2025-09-30 16:03:44
 */
@Slf4j
@ActiveProfiles("dev")
@SpringBootTest(classes = WebApplication.class)
public class RbacTest {
    @Autowired
    UserRepository userRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    public void updatePassword() {
        int userId = 1;
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            return;
        }

        String newPassword = "admin123456";
        String newSalt = RandomString.getSalt(64);
        String encodedNewPasswd = passwordEncoder.encode(newPassword + newSalt);

        user.setEncodedPassword(encodedNewPasswd);
        user.setSalt(newSalt);
        userRepository.save(user);
    }

    @Autowired
    MenuService menuService;
    @Autowired
    MenuRepository menuRepository;
    @Autowired
    ApplicationContext applicationContext;
    @Test
    public void menuTest() {
        List<Menu> menuList = new ArrayList<>();
        Map<String, Menu> groupMap = menuRepository.findByDeletedIsFalseAndType(MenuType.DIR.name()).stream()
                .collect(Collectors.groupingBy(Menu::getUrl,
                        Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))));

        Object bean = applicationContext.getBean("requestMappingHandlerMapping");
        if (!(bean instanceof RequestMappingHandlerMapping requestMappingHandlerMapping)) {
            return;
        }

        Set<String> allUrls = new HashSet<>();
        Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> mappingInfoHandlerMethodEntry : map.entrySet()) {
            RequestMappingInfo requestMappingInfo = mappingInfoHandlerMethodEntry.getKey();
            HandlerMethod handlerMethod = mappingInfoHandlerMethodEntry.getValue();

            List<String> requestMethods = requestMappingInfo.getMethodsCondition().getMethods().stream()
                    .map(RequestMethod::name)
                    .collect(Collectors.toList());
            List<String> urls = new ArrayList<>(requestMappingInfo.getPatternValues());
            String requestMethod = "";
            if (!requestMethods.isEmpty()) {
                requestMethod = requestMethods.get(0).toLowerCase(Locale.ROOT);
            }
            String url = urls.get(0).substring(1);
            String uniqueUrl = String.format("%s %s", requestMethod, url);
            if (!allUrls.add(uniqueUrl)) {
                log.info("url {} exist", uniqueUrl);
            }

            Class<?> clazz = handlerMethod.getMethod().getDeclaringClass();
            Method method = handlerMethod.getMethod();

            String className = clazz.getName();
            String methodName = method.getName();
            Annotation[] classAnnotations = clazz.getDeclaredAnnotations();
            Annotation[] methodAnnotations = method.getDeclaredAnnotations();

            if (className.equals(LoginController.class.getName())) {
                log.info("skip LoginController");
                continue;
            } else if (className.equals(ControllerErrorHandler.class.getName())) {
                log.info("skip ControllerErrorHandler");
                continue;
            } else if (url.startsWith("v3") || url.startsWith("swagger")) {
                log.info("skip swagger");
                continue;
            }

            String menuName = methodName;
            MenuType menuType = MenuType.GET;
            boolean visible = true;
            for (Annotation annotation : methodAnnotations) {
                if (annotation instanceof Operation) {
                    Operation operation = (Operation) annotation;
                    menuName = operation.summary();
                } else if (annotation instanceof PostMapping) {
                    menuType = MenuType.POST;
                } else if (annotation instanceof ResponseBody) {
                    visible = false;
                }
            }

            int pid = 0;
            Menu menu = menuRepository.findByDeletedIsFalseAndTypeAndUrl(menuType.name(), url);
            if (menu == null) {
                log.info("{} not exist", url);
                for (String parent : groupMap.keySet()) {
                    if (url.startsWith(parent)) {
                        pid = groupMap.get(parent).getId();
                    }
                }

                Menu menu1 = new Menu(menuType, visible, menuName, url, pid);
                menuList.add(menu1);
            }
        }

        //menuRepository.saveAll(menuList);
        System.out.println();

        List<Menu> allMenus = menuRepository.findAllByDeleted(false).stream()
                .filter(menu -> !menu.getType().equals(MenuType.DIR.name()))
                .collect(Collectors.toList());
        for (Menu menu : allMenus) {
            String type = menu.getType();
            String url = menu.getUrl();
            String key = String.format("%s %s", type, url).toLowerCase();
            if (!allUrls.contains(key)) {
                log.info("{} not exit in current url set", key);
                //menuService.deleteMenu(menu.getId());
            }
        }
    }

    @Autowired
    RoleRepository roleRepository;
    @Test
    public void roleTest() {
        for (RoleType roleType : RoleType.values()) {
            Role role = new Role(roleType);
            Role roleEntity = roleRepository.findByName(role.getName());
            if (roleEntity == null) {
                //roleRepository.save(role);
            } else {
                log.info("role {} exist", role.getName());
            }
        }
    }

    @Test
    public void getAllUrlsTest() {
        List<Map<String, String>> resList = new ArrayList<>();
        Object bean = applicationContext.getBean("requestMappingHandlerMapping");
        if (!(bean instanceof RequestMappingHandlerMapping requestMappingHandlerMapping)) {
            return;
        }

        Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> mappingInfoHandlerMethodEntry : map.entrySet()) {
            Map<String, String> resultMap = new LinkedHashMap<>();
            RequestMappingInfo requestMappingInfo = mappingInfoHandlerMethodEntry.getKey();
            HandlerMethod handlerMethod = mappingInfoHandlerMethodEntry.getValue();
            resultMap.put("className", handlerMethod.getMethod().getDeclaringClass().getName());
            Annotation[] declaredAnnotations = handlerMethod.getMethod().getDeclaredAnnotations();

            /*Annotation[] parentAnnotations = handlerMethod.getBeanType().getAnnotations();
            for (Annotation annotation : parentAnnotations) {
                if (annotation instanceof Api) {
                    Api api = (Api) annotation;
                    resultMap.put("classDesc", api.value());
                } else if (annotation instanceof RequestMapping) {
                    RequestMapping requestMapping = (RequestMapping) annotation;
                    if (null != requestMapping.value() && requestMapping.value().length > 0) {
                        resultMap.put("classURL", requestMapping.value()[0]);
                    }
                }
            }*/
            resultMap.put("methodName", handlerMethod.getMethod().getName());
            /*Annotation[] annotations = handlerMethod.getMethod().getDeclaredAnnotations();
            if (annotations != null) {
                for (Annotation annotation : annotations) {
                    if (annotation instanceof ApiOperation) {
                        ApiOperation methodDesc = (ApiOperation) annotation;
                        String desc = methodDesc.value();
                        resultMap.put("methodDesc", desc);
                    }
                }
            }*/

            PatternsRequestCondition p = requestMappingInfo.getPatternsCondition();
            for (String url : p.getPatterns()) {
                resultMap.put("methodURL", url);
            }

            RequestMethodsRequestCondition methodsRequestCondition = requestMappingInfo.getMethodsCondition();
            for (RequestMethod requestMethod : methodsRequestCondition.getMethods()) {
                resultMap.put("requestType", requestMethod.toString());
            }

            resList.add(resultMap);
        }

        List<ReqUrl> list = map.keySet().stream().map(requestMappingInfo -> {
            List<String> list1 = new ArrayList<>(requestMappingInfo.getPatternsCondition().getPatterns());
            List<RequestMethod> list2 = new ArrayList<>(requestMappingInfo.getMethodsCondition().getMethods());
            String url = list1.get(0);
            if (url.equals("/error") || url.startsWith("/swagger")) {
                return null;
            }

            String method = list2.get(0).name();
            return new ReqUrl(method, url);
        }).filter(Objects::nonNull).collect(Collectors.toList());

        System.out.println();
    }

    @AllArgsConstructor
    @Getter
    static class ReqUrl {
        private String method;
        private String url;

        @Override
        public String toString() {
            return url;
        }
    }

    @Autowired
    JdbcIndexedSessionRepository sessionRepository;
    @Autowired
    AccountService accountService;
    @Autowired
    AccountSessionService accountSessionService;
    @Test
    public void sessionTest() {
        accountService.getUserVOByPage(PageRequest.of(0, 100)).forEach(userVO -> {
            int userId = userVO.getUserId();
            accountSessionService.getUserSession(userId).forEach(userSession -> {
                String sessionId = userSession.getSessionId();
                sessionRepository.deleteById(sessionId);
            });
        });
    }
}
