package com.ecep.contract;

import java.time.Duration;
import java.util.Locale;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationHook;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.boot.context.metrics.buffering.StartupTimeline;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.metrics.StartupStep;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.data.web.config.EnableSpringDataWebSupport.PageSerializationMode;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

import com.ecep.contract.cloud.CloudRepositoriesConfig;
import com.ecep.contract.ds.DsRepositoriesConfig;
import com.ecep.contract.util.TaskMonitorCenter;

@SpringBootApplication(exclude = {
        org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration.class,
        RedisReactiveAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class
})
@EnableScheduling
@EnableAsync
@EnableCaching

@EnableSpringDataWebSupport(pageSerializationMode = PageSerializationMode.VIA_DTO)
public class SpringApp {

    private final TaskMonitorCenter taskMonitorCenter;
    private static final Logger logger = LoggerFactory.getLogger(SpringApp.class);

    static SpringApplication application;
    static ConfigurableApplicationContext context;

    SpringApp(TaskMonitorCenter taskMonitorCenter) {
        this.taskMonitorCenter = taskMonitorCenter;
    }

    public static <T> T getBean(Class<T> requiredType) throws BeansException {
        return context.getBean(requiredType);
    }

    public static Object getBean(String name) throws BeansException {
        return context.getBean(name);
    }

    public static ConfigurableListableBeanFactory getBeanFactory() {
        return context.getBeanFactory();
    }

    public static void launch(Properties properties, MessageHolder holder) {

        //
        holder.debug("应用程序环境准备中...");
        SpringApplication.withHook(new Hook(holder), () -> {
            // 动态地注册或修改这些组件和配置
            application.addBootstrapRegistryInitializer(registry -> {
                //
                System.out.println("registry = " + registry);

            });
            application.addListeners(event -> {
                logger.debug("SpringApp.launch ApplicationListener, event:{}", event);
            });

            application.addInitializers(app -> {
                logger.debug("SpringApp.launch ApplicationContextInitializer");
                ConfigurableEnvironment environment = app.getEnvironment();
                logger.debug("environment = {}", environment);
                PropertySource<?> dynamicProperties = environment.getPropertySources().get("dynamicProperties");
                if (dynamicProperties != null) {
                    logger.debug("dynamicProperties = {}", dynamicProperties);
                }
                environment.getPropertySources().addLast(new PropertiesPropertySource("dynamicProperties", properties));
                // app.getBeanFactory().registerSingleton("dataSource", dataSource());
                logger.debug("app = {}", app);

                if (app instanceof AnnotationConfigApplicationContext ctx) {
                    ctx.register(DsRepositoriesConfig.class);
                    ctx.register(CloudRepositoriesConfig.class);
                }
            });

            // startup.start("");
            context = application.run();
            logger.debug("SpringApp.launch application.run().");
            // Duration between =
            // Duration.between(startup.getBufferedTimeline().getStartTime(),
            // Instant.now());
            // holder.info("应用程序环境加载完成... " + between);
        });
        // CompletableFuture.runAsync(() -> {
        // // 在这里调用 startup 性能分析
        // analyzeStartupPerformance(startup);
        // }, Desktop.instance.getExecutorService());
    }

    /**
     * 分析启动性能数据并输出到日志
     */
    private static void analyzeStartupPerformance(BufferingApplicationStartup startup) {
        // 获取所有记录的事件
        StartupTimeline timeline = startup.getBufferedTimeline();
        if (timeline == null || timeline.getEvents().isEmpty()) {
            logger.warn("StartupTimeline 为空或没有事件！");
            return;
        }
        logger.info("总共有 {} 个事件", timeline.getEvents().size());

        // 找出与 Bean 初始化相关的步骤
        timeline.getEvents().stream()
                .filter(event -> event.getStartupStep().getName().startsWith("spring.beans."))
                .sorted((a, b) -> Long.compare(b.getDuration().toMillis(), a.getDuration().toMillis()))
                .limit(30)
                .forEach(event -> {
                    String name = event.getStartupStep().getName();
                    long duration = event.getDuration().toMillis();
                    logger.info("Bean 初始化阶段: {} - 耗时: {} ms", name, duration);

                    for (StartupStep.Tag tag : event.getStartupStep().getTags()) {
                        if ("beanName".equals(tag.getKey())) {
                            logger.info("  └── Bean 名称: {}", tag.getValue());
                        }
                    }
                });
    }

    public static String getMessage(String code, Object[] args, Locale locale) {
        return context.getMessage(code, args, locale);
    }

    public static void shutdown() {
        System.out.println("SpringApp.shutdown");
        if (logger.isDebugEnabled()) {
            logger.debug("shutdown");
        }
        if (context != null) {
            if (context.isRunning()) {
                context.close();
            }
        }
    }

    public static boolean isRunning() {
        return context != null && context.isRunning();
    }

    static class Hook implements SpringApplicationHook, SpringApplicationRunListener {
        MessageHolder holder;

        Hook(MessageHolder holder) {
            this.holder = holder;
        }

        public void debug(String msg) {
            holder.debug(msg);
        }

        @Override
        public void starting(ConfigurableBootstrapContext bootstrapContext) {
            logger.debug("Desktop.starting");
            debug("Spring Application 启动中...");
        }

        @Override
        public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
                ConfigurableEnvironment environment) {
            logger.debug("Desktop.environmentPrepared");
            debug("初始化 Environment 中，请稍后...");
        }

        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
            logger.debug("Desktop.contextPrepared");
            debug("Spring Application Context 预处理中，请稍后...");
        }

        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
            logger.debug("Desktop.contextLoaded");
            debug("Spring Application Context 初始化完毕，请稍后...");
        }

        @Override
        public void started(ConfigurableApplicationContext context, Duration timeTaken) {
            logger.debug("Desktop.started");
            debug("Spring Application 启动完毕.");
        }

        @Override
        public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
            logger.debug("Desktop.ready");
            debug("Spring Application ready.");
        }

        @Override
        public void failed(ConfigurableApplicationContext context, Throwable exception) {
            logger.error("Desktop.failed", exception);
            holder.error("Spring Application 启动失败(" + exception.getMessage() + ").");
        }

        @Override
        public SpringApplicationRunListener getRunListener(SpringApplication springApplication) {
            return this;
        }
    }

    @EventListener
    public void handleClosedEvent(ContextClosedEvent event) {
        if (logger.isDebugEnabled()) {
            logger.debug("handleClosedEvent={}", event);
        }
    }

    public static TaskMonitorCenter getTaskMonitorCenter() {
        return getBean(TaskMonitorCenter.class);
    }

    @Bean
    public ScheduledExecutorService scheduledExecutorService() {
        return Executors.newScheduledThreadPool(3);
    }

}
