package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/edu-cloud/api-gateway/internal/config"
	"github.com/edu-cloud/api-gateway/internal/health"
	"github.com/edu-cloud/api-gateway/internal/middleware"
	"github.com/edu-cloud/api-gateway/internal/observability"
	"github.com/edu-cloud/api-gateway/internal/proxy"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)

// maxBodyBytes 请求体大小上限：10MB
const maxBodyBytes int64 = 10 * 1024 * 1024

func main() {
	cfg := config.Load()
	// 初始化 OpenTelemetry tracer（endpoint 为空时自动跳过）
	tracerShutdown := observability.InitTracer("api-gateway", cfg.OTLPEndpoint)
	defer tracerShutdown()

	gin.SetMode(gin.ReleaseMode)
	r := gin.New()
	// 关闭尾斜杠重定向：避免 Next.js rewrites 代理时 /api/v1/classes → 301 → /api/v1/classes/ 循环
	r.RedirectTrailingSlash = false

	// 全局中间件（按顺序注册）
	// 1. panic 恢复（最外层，捕获后续所有中间件与 handler 的 panic）
	r.Use(middleware.Recovery())
	// 2. OpenTelemetry 自动埋点（HTTP 请求/响应 span）
	r.Use(otelgin.Middleware("api-gateway"))
	// 3. 请求 ID 注入
	r.Use(middleware.RequestID())
	// 4. 跨域
	r.Use(middleware.CORS())
	// 5. 安全响应头
	r.Use(middleware.SecurityHeaders())
	// 6. 请求体大小限制
	r.Use(middleware.RequestBodyLimit(maxBodyBytes))
	// 7. 限流（每 IP 100 rps，突发 20）
	r.Use(middleware.RateLimit(100, 20))

	// 健康检查路由（无需鉴权，在 Auth 之前）
	r.GET("/healthz", health.Healthz)
	r.GET("/readyz", health.Readyz)
	// Prometheus 指标端点
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))

	// API v1 组：熔断 + 鉴权 + 反向代理
	api := r.Group("/api/v1")
	// 7. 熔断（仅作用于代理路由，下游 5xx 触发）
	api.Use(middleware.CircuitBreaker("downstream"))
	// 8. JWT 鉴权
	api.Use(middleware.AuthMiddleware(cfg))
	{
		// classes 服务路由
		// 注：同时注册无尾斜杠与通配符两条路由。RedirectTrailingSlash=false 时，
		// Gin 不会自动把 /classes 跳到 /classes/，所以两条都要显式注册。
		classesProxy, err := proxy.NewProxy(cfg.ClassesServiceURL)
		if err != nil {
			log.Fatalf("failed to create classes proxy: %v", err)
		}
		classesHandler := proxy.ProxyHandler(classesProxy)
		api.Any("/classes", classesHandler)
		api.Any("/classes/*path", classesHandler)

		// IAM 服务路由（身份与访问管理）
		iamProxy, err := proxy.NewProxy(cfg.IamServiceURL)
		if err != nil {
			log.Fatalf("failed to create iam proxy: %v", err)
		}
		iamHandler := proxy.ProxyHandler(iamProxy)
		api.Any("/iam", iamHandler)
		api.Any("/iam/*path", iamHandler)

		// Teacher BFF 路由（教师聚合层）
		bffProxy, err := proxy.NewProxy(cfg.TeacherBffURL)
		if err != nil {
			log.Fatalf("failed to create teacher-bff proxy: %v", err)
		}
		bffHandler := proxy.ProxyHandler(bffProxy)
		api.Any("/teacher", bffHandler)
		api.Any("/teacher/*path", bffHandler)

		// core-edu 服务路由（考试/作业/成绩）
		// 注：同时注册无尾斜杠与通配符两条路由，与 classes/iam/teacher 一致。
		coreEduProxy, err := proxy.NewProxy(cfg.CoreEduServiceURL)
		if err != nil {
			log.Fatalf("failed to create core-edu proxy: %v", err)
		}
		coreEduHandler := proxy.ProxyHandler(coreEduProxy)
		api.Any("/exams", coreEduHandler)
		api.Any("/exams/*path", coreEduHandler)
		api.Any("/homework", coreEduHandler)
		api.Any("/homework/*path", coreEduHandler)
		api.Any("/grades", coreEduHandler)
		api.Any("/grades/*path", coreEduHandler)

		// content 服务路由（教材/章节/知识点/题库）
		contentProxy, err := proxy.NewProxy(cfg.ContentServiceURL)
		if err != nil {
			log.Fatalf("failed to create content proxy: %v", err)
		}
		contentHandler := proxy.ProxyHandler(contentProxy)
		api.Any("/textbooks", contentHandler)
		api.Any("/textbooks/*path", contentHandler)
		api.Any("/chapters", contentHandler)
		api.Any("/chapters/*path", contentHandler)
		api.Any("/knowledge-points", contentHandler)
		api.Any("/knowledge-points/*path", contentHandler)
		api.Any("/questions", contentHandler)
		api.Any("/questions/*path", contentHandler)

		// msg 服务路由（通知/消息）
		msgProxy, err := proxy.NewProxy(cfg.MsgServiceURL)
		if err != nil {
			log.Fatalf("failed to create msg proxy: %v", err)
		}
		msgHandler := proxy.ProxyHandler(msgProxy)
		api.Any("/notifications", msgHandler)
		api.Any("/notifications/*path", msgHandler)

		// ai 服务路由（AI 聊天/生成/优化）
		aiProxy, err := proxy.NewProxy(cfg.AiServiceURL)
		if err != nil {
			log.Fatalf("failed to create ai proxy: %v", err)
		}
		aiHandler := proxy.ProxyHandler(aiProxy)
		api.Any("/ai", aiHandler)
		api.Any("/ai/*path", aiHandler)

		// data-ana 服务路由（学情诊断/错题本）
		dataAnaProxy, err := proxy.NewProxy(cfg.DataAnaServiceURL)
		if err != nil {
			log.Fatalf("failed to create data-ana proxy: %v", err)
		}
		dataAnaHandler := proxy.ProxyHandler(dataAnaProxy)
		api.Any("/analytics", dataAnaHandler)
		api.Any("/analytics/*path", dataAnaHandler)
	}

	srv := &http.Server{
		Addr:         ":" + cfg.Port,
		Handler:      r,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 30 * time.Second,
	}

	// 优雅关闭
	go func() {
		log.Printf("API Gateway listening on :%s", cfg.Port)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s", err)
		}
	}()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Println("Shutting down API Gateway...")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server forced to shutdown:", err)
	}
	log.Println("API Gateway exited")
}
