package com.example.scaffold.learning;

import com.example.scaffold.aop.PerformanceAspect;
import com.example.scaffold.learning.lifecycle.IocLifecycleTracker;
import com.example.scaffold.learning.lifecycle.LifecycleDemoBean;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.WebApplicationContext;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.util.*;

/**
 * IoC 容器学习控制器
 * 
 * 学习要点：
 * 1. Bean 的生命周期
 * 2. 依赖注入方式
 * 3. Bean 作用域
 * 4. 条件化配置
 */
@RestController
@RequestMapping("/api/learning/ioc")
@RequiredArgsConstructor
public class IocLearningController {
    
    private final ApplicationContext applicationContext;
    private final PerformanceAspect performanceAspect;
    private final IocLifecycleTracker lifecycleTracker;
    
    // 演示字段注入（不推荐，但可以用）
    // 注意：session scope 的 bean 需要在 web 上下文中使用
    // 这里仅作演示，实际使用时通过方法参数获取
    
    /**
     * 查看所有 Bean
     */
    @GetMapping("/beans")
    public Map<String, Object> listBeans() {
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("total", beanNames.length);
        result.put("userBeans", Arrays.stream(beanNames)
                .filter(name -> name.startsWith("user") || name.startsWith("learning") || 
                        name.contains("Service") || name.contains("Controller") || name.contains("Mapper"))
                .sorted()
                .toList());
        result.put("allBeans", Arrays.stream(beanNames).sorted().toList());
        
        return result;
    }
    
    /**
     * 查看 Bean 详情
     */
    @GetMapping("/beans/{name}")
    public Map<String, Object> getBeanDetail(@PathVariable String name) {
        try {
            Object bean = applicationContext.getBean(name);
            Class<?> clazz = bean.getClass();
            
            return Map.of(
                "name", name,
                "type", clazz.getName(),
                "simpleName", clazz.getSimpleName(),
                "interfaces", Arrays.toString(clazz.getInterfaces()),
                "annotations", Arrays.toString(clazz.getAnnotations()),
                "scope", applicationContext.isSingleton(name) ? "singleton" : "prototype"
            );
        } catch (BeansException e) {
            return Map.of("error", "Bean not found: " + name);
        }
    }
    
    /**
     * 演示依赖注入方式
     */
    @GetMapping("/injection-types")
    public Map<String, String> injectionTypes() {
        return Map.of(
            "构造器注入", "推荐！明确依赖，不可变，易于测试",
            "Setter注入", "可选依赖，灵活性高",
            "字段注入", "不推荐！隐藏依赖，难以测试",
            "本控制器使用", "构造器注入 (RequiredArgsConstructor) + 字段注入演示"
        );
    }
    
    /**
     * 演示 Bean 作用域
     */
    @GetMapping("/scopes")
    public Map<String, Object> scopes() {
        return Map.of(
            "singleton", "单例 - 整个应用只有一个实例（默认）",
            "prototype", "原型 - 每次请求都创建新实例",
            "request", "请求 - 每个 HTTP 请求一个实例",
            "session", "会话 - 每个 HTTP 会话一个实例",
            "lazySingleton", "懒加载单例 - 第一次真正获取时才创建",
            "tip", "session scope 需要在 web 请求上下文中使用"
        );
    }
    
    /**
     * IoC 生命周期总览
     */
    @GetMapping("/lifecycle/overview")
    public Map<String, Object> lifecycleOverview() {
        return Map.of(
                "jvmClassLoading", List.of(
                        "类第一次被引用时才可能触发加载",
                        "static 代码块在类初始化阶段执行一次",
                        "类加载成功后，JVM 里就有这个 Class 元数据"
                ),
                "springBeanLifecycle", List.of(
                        "扫描 BeanDefinition",
                        "实例化 Bean（singleton 通常在启动阶段）",
                        "依赖注入",
                        "@PostConstruct 初始化",
                        "放入单例池或按 scope 管理",
                        "容器关闭时销毁 singleton Bean"
                ),
                "coreIdea", "类加载 ≠ Bean 创建；Bean 创建 ≠ 每次请求都 new"
        );
    }

    @GetMapping("/lifecycle/timeline")
    public List<Map<String, Object>> lifecycleTimeline() {
        return lifecycleTracker.snapshot();
    }

    @PostMapping("/lifecycle/reset")
    public Map<String, String> resetLifecycleTimeline() {
        lifecycleTracker.reset();
        return Map.of("status", "ok", "message", "生命周期时间线已重置");
    }

    @GetMapping("/lifecycle/inspect/{scopeType}")
    public Map<String, Object> inspectLifecycleBean(@PathVariable String scopeType,
                                                    @RequestParam(defaultValue = "manual-check") String trigger) {
        String beanName = switch (scopeType) {
            case "prototype" -> "iocPrototypeLifecycleBean";
            case "lazy" -> "iocLazySingletonLifecycleBean";
            default -> "iocSingletonLifecycleBean";
        };
        LifecycleDemoBean bean = (LifecycleDemoBean) applicationContext.getBean(beanName);
        Map<String, Object> result = new LinkedHashMap<>(bean.inspect(trigger));
        result.put("beanNameFromContext", beanName);
        result.put("isSingletonInContext", applicationContext.isSingleton(beanName));
        result.put("timelineSize", lifecycleTracker.snapshot().size());
        return result;
    }

    @GetMapping("/lifecycle/compare")
    public Map<String, Object> compareScopes() {
        LifecycleDemoBean singleton1 = (LifecycleDemoBean) applicationContext.getBean("iocSingletonLifecycleBean");
        LifecycleDemoBean singleton2 = (LifecycleDemoBean) applicationContext.getBean("iocSingletonLifecycleBean");
        LifecycleDemoBean prototype1 = (LifecycleDemoBean) applicationContext.getBean("iocPrototypeLifecycleBean");
        LifecycleDemoBean prototype2 = (LifecycleDemoBean) applicationContext.getBean("iocPrototypeLifecycleBean");
        LifecycleDemoBean lazy1 = (LifecycleDemoBean) applicationContext.getBean("iocLazySingletonLifecycleBean");
        LifecycleDemoBean lazy2 = (LifecycleDemoBean) applicationContext.getBean("iocLazySingletonLifecycleBean");

        return Map.of(
                "singleton", List.of(singleton1.inspect("compare-1"), singleton2.inspect("compare-2")),
                "prototype", List.of(prototype1.inspect("compare-1"), prototype2.inspect("compare-2")),
                "lazySingleton", List.of(lazy1.inspect("compare-1"), lazy2.inspect("compare-2"))
        );
    }

    /**
     * 性能统计
     */
    @GetMapping("/performance")
    public Map<String, Object> getPerformance() {
        var stats = performanceAspect.getStats();
        Map<String, Object> result = new LinkedHashMap<>();
        
        stats.forEach((key, value) -> {
            long totalMs = value.totalTime.get() / 1_000_000;
            long avgMs = value.totalCount.get() > 0 ? totalMs / value.totalCount.get() : 0;
            
            result.put(key, Map.of(
                "count", value.totalCount.get(),
                "errors", value.errorCount.get(),
                "totalMs", totalMs,
                "avgMs", avgMs,
                "maxMs", value.maxTime.get()
            ));
        });
        
        return result;
    }
    
    /**
     * 重置性能统计
     */
    @PostMapping("/performance/reset")
    public Map<String, String> resetPerformance() {
        performanceAspect.resetStats();
        return Map.of("status", "ok", "message", "性能统计已重置");
    }
    
    /**
     * 学习 Bean - 演示作用域和生命周期
     */
    @org.springframework.stereotype.Component("learningBean")
    @Scope(WebApplicationContext.SCOPE_SESSION)
    public static class LearningBean {
        private final String instanceId = UUID.randomUUID().toString().substring(0, 8);
        private int accessCount = 0;
        
        @PostConstruct
        public void init() {
            System.out.println("🟢 [LearningBean @PostConstruct] Bean 初始化: " + instanceId);
        }
        
        @PreDestroy
        public void destroy() {
            System.out.println("🔴 [LearningBean @PreDestroy] Bean 销毁: " + instanceId);
        }
        
        public String getInstanceInfo() {
            accessCount++;
            return String.format("实例ID: %s, 访问次数: %d, 作用域: session", instanceId, accessCount);
        }
    }
}