package com.njzscloud.dispose.sys.endpoint.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.utils.GroupUtil;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import com.njzscloud.common.security.contant.EndpointAccessModel;
import com.njzscloud.dispose.sys.endpoint.contant.RequestMethod;
import com.njzscloud.dispose.sys.endpoint.mapper.EndpointMapper;
import com.njzscloud.dispose.sys.endpoint.pojo.entity.EndpointEntity;
import com.njzscloud.dispose.sys.endpoint.pojo.param.AddEndpointParam;
import com.njzscloud.dispose.sys.endpoint.pojo.param.EndpointSearchParam;
import com.njzscloud.dispose.sys.endpoint.pojo.param.ModifyEndpointParam;
import com.njzscloud.dispose.sys.endpoint.pojo.result.EndpointDetailResult;
import com.njzscloud.dispose.sys.resource.contant.ResourceOrigin;
import com.njzscloud.dispose.sys.resource.pojo.entity.ResourceEntity;
import com.njzscloud.dispose.sys.resource.pojo.result.ControllerMappingResult;
import com.njzscloud.dispose.sys.resource.service.ResourceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.method.HandlerMethod;
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.util.*;
import java.util.stream.Collectors;

/**
 * 端点信息表
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class EndpointService extends ServiceImpl<EndpointMapper, EndpointEntity> implements IService<EndpointEntity> {
    private final ResourceService resourceService;
    private final RequestMappingHandlerMapping requestMappingHandlerMapping;

    /**
     * 新增
     */
    @Transactional(rollbackFor = Exception.class)
    public void add(AddEndpointParam addEndpointParam) {
        EndpointEntity endpointEntity = BeanUtil.copyProperties(addEndpointParam, EndpointEntity.class);
        RequestMethod requestMethod = addEndpointParam.getRequestMethod();
        String endpointPath = addEndpointParam.getEndpointPath();
        boolean exists = this.exists(Wrappers.<EndpointEntity>lambdaQuery()
            .eq(EndpointEntity::getRequestMethod, requestMethod)
            .eq(EndpointEntity::getEndpointPath, endpointPath)
        );
        Assert.isFalse(exists, () -> Exceptions.exception("端点重复"));

        this.save(endpointEntity);
        String sn = requestMethod + endpointPath;

        resourceService.save(new ResourceEntity()
            .setSn(sn)
            .setDataId(endpointEntity.getId())
            .setTableName(ResourceOrigin.Endpoint.getVal())
            .setMemo("端点资源-" + requestMethod + " " + endpointPath));
    }

    /**
     * 修改
     */
    @Transactional(rollbackFor = Exception.class)
    public void modify(ModifyEndpointParam modifyEndpointParam) {
        Long id = modifyEndpointParam.getId();
        EndpointEntity oldData = this.getById(id);
        Assert.notNull(oldData, () -> Exceptions.exception("要修改的数据不存在"));

        RequestMethod requestMethod = modifyEndpointParam.getRequestMethod();
        String endpointPath = modifyEndpointParam.getEndpointPath();

        boolean exists = this.exists(Wrappers.<EndpointEntity>lambdaQuery()
            .eq(EndpointEntity::getRequestMethod, requestMethod)
            .eq(EndpointEntity::getEndpointPath, endpointPath)
            .ne(EndpointEntity::getId, id)
        );
        Assert.isFalse(exists, () -> Exceptions.exception("端点重复"));

        EndpointEntity endpointEntity = BeanUtil.copyProperties(modifyEndpointParam, EndpointEntity.class);
        this.updateById(endpointEntity);

        String sn = requestMethod + endpointPath;

        resourceService.update(Wrappers.<ResourceEntity>lambdaUpdate()
            .eq(ResourceEntity::getTableName, ResourceOrigin.Endpoint.getVal())
            .eq(ResourceEntity::getDataId, id)
            .set(ResourceEntity::getMemo, "端点资源-" + requestMethod + " " + endpointPath)
            .set(ResourceEntity::getSn, sn)
        );
    }

    /**
     * 删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void del(List<Long> ids) {
        boolean occupied = resourceService.occupied(ids, ResourceOrigin.Endpoint);
        Assert.isFalse(occupied, () -> Exceptions.exception("端点已被分配，不能删除"));

        this.removeBatchByIds(ids);
        resourceService.delRes(ids, ResourceOrigin.Endpoint);
    }

    /**
     * 详情
     */
    public EndpointDetailResult detail(Long id) {
        EndpointEntity endpointEntity = this.getById(id);
        return BeanUtil.copyProperties(endpointEntity, EndpointDetailResult.class);
    }

    /**
     * 分页查询
     */
    public PageResult<EndpointDetailResult> paging(PageParam pageParam, EndpointSearchParam endpointSearchParam) {
        String endpointPath = endpointSearchParam.getEndpointPath();
        return PageResult.of(this.page(pageParam.toPage(), Wrappers.<EndpointEntity>lambdaQuery()
            .like(StrUtil.isNotBlank(endpointPath), EndpointEntity::getEndpointPath, endpointPath)
            .orderByAsc(EndpointEntity::getRequestMethod, EndpointEntity::getRoutingPath, EndpointEntity::getEndpointPath)
        ).convert(it -> BeanUtil.copyProperties(it, EndpointDetailResult.class)));
    }

    /**
     * 查询所有
     */
    public List<EndpointDetailResult> listAll(EndpointSearchParam endpointSearchParam) {
        String endpointPath = endpointSearchParam.getEndpointPath();
        return this.list(Wrappers.<EndpointEntity>lambdaQuery()
                .like(StrUtil.isNotBlank(endpointPath), EndpointEntity::getEndpointPath, endpointPath)
                .orderByAsc(EndpointEntity::getRequestMethod, EndpointEntity::getRoutingPath, EndpointEntity::getEndpointPath)
            )
            .stream()
            .map(it -> BeanUtil.copyProperties(it, EndpointDetailResult.class))
            .collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveAll() {
        List<ControllerMappingResult> controllerMappingResults = scanAllControllerMappings();
        List<EndpointEntity> oldEndpoints = this.list();
        Map<String, EndpointEntity> map = GroupUtil.k_o(oldEndpoints, it -> it.getRequestMethod() + it.getEndpointPath());
        Set<String> endpointPaths = map.keySet();

        List<EndpointEntity> list = controllerMappingResults
            .stream()
            .filter(it -> it.getControllerClassName().startsWith("com.njzscloud") && !endpointPaths.contains(it.getHttpMethods() + it.getFullUrl()))
            .map(it -> new EndpointEntity()
                .setEndpointPath(it.getFullUrl())
                .setRoutingPath("")
                .setMemo("")
                .setAccessModel(EndpointAccessModel.LOGINED)
                .setRequestMethod(RequestMethod.resolve(it.getHttpMethods())))
            .toList();

        this.saveBatch(list);
        List<ResourceEntity> resourceEntities = list.stream().map(it -> {
            RequestMethod requestMethod = it.getRequestMethod();
            String endpointPath = it.getEndpointPath();
            return new ResourceEntity()
                .setSn(requestMethod + endpointPath)
                .setDataId(it.getId())
                .setTableName(ResourceOrigin.Endpoint.getVal())
                .setMemo("接口资源-" + requestMethod + " " + endpointPath)
                ;
        }).toList();

        resourceService.saveBatch(resourceEntities);
    }

    public List<ControllerMappingResult> scanAllControllerMappings() {
        List<ControllerMappingResult> result = new ArrayList<>();

        // 1. 获取所有RequestMappingInfo（包含映射规则）和对应的HandlerMethod
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();

        // 2. 遍历解析每个映射规则
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            RequestMappingInfo requestMappingInfo = entry.getKey();
            HandlerMethod handlerMethod = entry.getValue();

            // 封装DTO
            ControllerMappingResult dto = new ControllerMappingResult();

            // === 解析控制器类信息 ===
            Class<?> controllerClass = handlerMethod.getBeanType();
            dto.setControllerClassName(controllerClass.getCanonicalName());

            // === 解析方法级路径 ===
            Set<String> methodPatterns = requestMappingInfo.getPatternValues();

            // 方法路径通常只有一个，取第一个即可

            // === 拼接完整URL ===
            String fullUrl = methodPatterns.iterator().next();
            // 处理重复的/（如类路径/endpoint + 方法路径/add → /endpoint/add；类路径/endpoint/ + 方法路径/add → /endpoint/add）
            fullUrl = fullUrl.replaceAll("//+", "/");
            dto.setFullUrl(fullUrl);

            // === 解析请求方法（GET/POST等） ===
            RequestMethodsRequestCondition methodsCondition = requestMappingInfo.getMethodsCondition();
            Set<org.springframework.web.bind.annotation.RequestMethod> methods = methodsCondition.getMethods();
            Optional<org.springframework.web.bind.annotation.RequestMethod> first = methods.stream().findFirst();
            Optional<String> s = first.map(Enum::name);
            dto.setHttpMethods(s.orElse(""));

            // === 解析方法名 ===
            dto.setMethodName(handlerMethod.getMethod().getName());

            result.add(dto);
        }

        return result;
    }
}
