package com.sonic.sdk.api.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.ImmutableMap;
import com.sonic.sdk.api.annotation.IgnoreAuth;
import com.sonic.sdk.api.annotation.InternalRpc;
import com.sonic.sdk.api.vo.ServiceAuthRegist;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Slf4j
public class GateWayAuthScanRunner implements ApplicationRunner {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${spring.application.name}")
    private String applicationName;

    private String baseBackages;

    private static final String SECRET_KEY = "l5lUstfWcb3GlWqiiHJKVLKTSvwJSqrT";

    public GateWayAuthScanRunner(String baseBackages) {
        this.baseBackages = baseBackages;
    }

    @IgnoreAuth
    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        // true：默认TypeFilter生效，这种模式会查询出许多不符合你要求的class名
        // false：关闭默认TypeFilter
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        provider.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
        Set<BeanDefinition> beanDefinitionSet = provider.findCandidateComponents(this.baseBackages);
        List<String> ignoreAuthPathList = new ArrayList<String>();
        List<String> internalUrlList = new ArrayList<String>();
        List<String> allUrlList = new ArrayList<String>();
        log.info("开始扫描 非授权URL 和 黑名单 包名: {} .", baseBackages);
        for (BeanDefinition beanDefinition : beanDefinitionSet) {
            String classPth = "";
            Class clazz = Class.forName(beanDefinition.getBeanClassName());
            RequestMapping requestMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
            InternalRpc internalRpc = (InternalRpc) clazz.getAnnotation(InternalRpc.class);
            if (internalRpc != null) {
                internalUrlList.add(internalRpc.path());
            }

            if (requestMapping != null) {
                classPth = requestMapping.value()[0];
            }
            Method methods[] = clazz.getDeclaredMethods();
            for (Method method : methods) {
                String url = classPth + getMethodPath(method);
                //将非/api/和非/admin/开头的url接口加入到所有接口列表中
                if(StringUtils.isNotEmpty(url) && !url.equals("null") &&
                        !url.startsWith("/api/") && !url.startsWith("api/") && !url.startsWith("/admin/") && !url.startsWith("admin/")) {
                    allUrlList.add(url);
                }
                IgnoreAuth ignoreAuthMethod = method.getAnnotation(IgnoreAuth.class);
                if (ignoreAuthMethod == null) {
                    continue;
                }
                //忽略登录认证的接口，需要排除掉 /api/和/admin的数据这样减少一点内存的占用
                if(StringUtils.isNotEmpty(url) && !url.equals("null") &&
                        !url.startsWith("/api/") && !url.startsWith("api/") && !url.startsWith("/admin/") && !url.startsWith("admin/")) {
                    ignoreAuthPathList.add(url);
                }
            }
        }

        String env = SpringContextUtil.getActiveProfile();
        Map<String, String> hostMaps = ImmutableMap.of("dev", "http://localhost:8082",
                "local", "http://localhost:8082",
                "test", "http://test-gateway-svc:8080",
                "staging", "http://prod-gateway-svc:8080",
                "product", "http://prod-gateway-svc:8080"
        );
        String url = hostMaps.get(env) + "/api/gateway/auth/register";
        log.info("上报信息 url:{}", url);
        try {
            ServiceAuthRegist serviceAuthRegist = new ServiceAuthRegist();
            serviceAuthRegist.setApplicationName(this.applicationName);
            serviceAuthRegist.setIgnoreAuthList(ignoreAuthPathList);
            serviceAuthRegist.setUrlBlacklist(internalUrlList);
            serviceAuthRegist.setUrlAllList(allUrlList);
            String json = JSON.toJSONString(serviceAuthRegist);
            String content = AesEncodeUtil.encrypt(json, SECRET_KEY);
            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);
            Map<String, String> map = ImmutableMap.of("content", content);
            HttpEntity<String> requestEntity = new HttpEntity<String>(JSONObject.toJSONString(map), requestHeaders);
            String result = restTemplate.postForObject(url, requestEntity, String.class);
            log.info("===> 上报网关服务 结果: {}", result);
        } catch (Exception e) {
            log.error("上报网关服务 授权信息 异常", e);
        }
        log.info("上报网关服务 授权信息 结束");
    }

    private String getMethodPath(Method method) {
        PostMapping postMappingMethod = method.getAnnotation(PostMapping.class);
        if (postMappingMethod != null) {
            return postMappingMethod.value()[0];
        }
        GetMapping getMappingMethod = method.getAnnotation(GetMapping.class);
        if (getMappingMethod != null) {
            return getMappingMethod.value()[0];
        }
        DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
        if (deleteMapping != null) {
            return deleteMapping.value()[0];
        }

        PutMapping putMapping = method.getAnnotation(PutMapping.class);
        if (putMapping != null) {
            return putMapping.value()[0];
        }

        RequestMapping requestMappingMethod = method.getAnnotation(RequestMapping.class);
        if (requestMappingMethod != null) {
            return requestMappingMethod.value()[0];
        }
        return null;
    }

}
