// oci-portal 后端服务启动入口。
package main

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

	// 纯 Go 时区数据(+~450KB):distroless/scratch 容器无 tzdata,
	// 嵌入后 TZ 环境变量即可让 cron 按本地时区解释
	_ "time/tzdata"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	// swag 生成的 OpenAPI spec(init 时注册,SWAGGER=1 的 /swagger 路由消费)
	_ "oci-portal/docs"
	"oci-portal/internal/api"
	"oci-portal/internal/config"
	"oci-portal/internal/crypto"
	"oci-portal/internal/database"
	"oci-portal/internal/oci"
	"oci-portal/internal/service"
)

// @title OCI Portal API
// @version 0.0.1
// @description 自托管 OCI 多租户管理面板 API。业务接口用 JWT(Bearer);AI 网关端点(/ai/v1/*)用独立网关密钥(Authorization: Bearer 或 x-api-key)。
// @BasePath /
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
// @description 登录接口返回的 JWT,格式: Bearer <token>
func init() {
	if os.Getenv("GIN_MODE") == "" {
		gin.SetMode(gin.ReleaseMode)
	}
}

func main() {
	healthcheck := flag.Bool("healthcheck", false, "探活本机服务后退出:健康 0,异常 1(容器 HEALTHCHECK 用)")
	flag.Parse()
	if *healthcheck {
		os.Exit(runHealthcheck())
	}
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

// runHealthcheck 请求本机 HTTP 端口:能响应且非 5xx 视为健康。
// distroless 镜像无 shell/wget,靠二进制自检 flag 支撑 HEALTHCHECK。
func runHealthcheck() int {
	addr := os.Getenv("ADDR")
	if addr == "" {
		addr = ":8080"
	}
	_, port, err := net.SplitHostPort(addr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "healthcheck: bad ADDR %q: %v\n", addr, err)
		return 1
	}
	client := &http.Client{Timeout: 3 * time.Second}
	resp, err := client.Get("http://127.0.0.1:" + port + "/")
	if err != nil {
		fmt.Fprintf(os.Stderr, "healthcheck: %v\n", err)
		return 1
	}
	defer resp.Body.Close()
	if resp.StatusCode >= http.StatusInternalServerError {
		fmt.Fprintf(os.Stderr, "healthcheck: status %d\n", resp.StatusCode)
		return 1
	}
	return 0
}

// bootstrap 完成配置加载、数据库打开与加密组件构建。
func bootstrap() (*config.Config, *gorm.DB, *crypto.Cipher, error) {
	cfg, err := config.Load()
	if err != nil {
		return nil, nil, nil, err
	}
	db, err := database.Open(cfg.DBDriver, cfg.DBDSN, cfg.DBPath)
	if err != nil {
		return nil, nil, nil, err
	}
	cipher, err := crypto.NewCipher(cfg.DataKey)
	if err != nil {
		return nil, nil, nil, err
	}
	return cfg, db, cipher, nil
}

func run() error {
	cfg, db, cipher, err := bootstrap()
	if err != nil {
		return err
	}
	auth := service.NewAuthService(db, cfg.JWTSecret)
	auth.SetCipher(cipher)
	if err := auth.EnsureAdmin(cfg.AdminUsername, cfg.AdminPassword); err != nil {
		return err
	}
	ociClient := oci.NewCachedClient(oci.NewClient())
	ociConfigs := service.NewOciConfigService(db, cipher, ociClient)
	settings := service.NewSettingService(db, cipher)
	settings.SetEnvPublicURL(cfg.PublicURL)
	if err := settings.ReloadSecurity(context.Background()); err != nil {
		return err
	}
	notifier := service.NewNotifier(settings)
	auth.SetNotifier(notifier, settings)
	oauth := service.NewOAuthService(db, settings, auth)
	systemLogs := service.NewSystemLogService(db)
	logEvents := service.NewLogEventService(db)
	logEvents.SetRelayDeps(ociConfigs, ociClient, cfg.PublicURL)
	logEvents.SetNotifier(notifier, settings)
	cleanupCtx, stopCleanup := context.WithCancel(context.Background())
	systemLogs.StartCleanup(cleanupCtx)
	logEvents.StartParser(cleanupCtx)
	logEvents.StartCleanup(cleanupCtx)
	aiGateway := service.NewAiGatewayService(db, ociConfigs, ociClient)
	aiGateway.StartCleanup(cleanupCtx)
	// defer 为 LIFO：先停调度与清理，再等在途通知与日志写完
	defer logEvents.Wait()
	defer systemLogs.Wait()
	defer notifier.Wait()
	defer aiGateway.Wait()
	defer stopCleanup()
	tasks := service.NewTaskService(db, ociConfigs, notifier, settings)
	ociConfigs.SetTenantCleanupDeps(tasks)
	tasks.AttachAiGateway(aiGateway)
	aiGateway.SetOnChannelsChanged(tasks.SyncAiProbeTask)
	if err := tasks.Start(); err != nil {
		return err
	}
	defer tasks.Stop()
	// 启动时对齐一次探测任务(已有渠道而任务缺失/暂停时自动建立或激活)
	tasks.SyncAiProbeTask(context.Background())
	console := service.NewConsoleService(ociConfigs)
	proxies := service.NewProxyService(db, cipher)
	defer proxies.Wait()
	// 「关于」页存储指标需知数据库形态
	api.SetAboutRuntime(cfg.DBDriver, cfg.DBPath)
	router := api.NewRouter(auth, oauth, ociConfigs, tasks, console, settings, notifier, systemLogs, logEvents, proxies, aiGateway)
	return serveHTTP(cfg.Addr, router)
}

// serveHTTP 以显式超时与请求头上限启动 HTTP 服务,并在 SIGINT/SIGTERM 时
// 优雅关闭(10 秒排空);防慢连接(Slowloris)与超大头耗尽资源。
// WriteTimeout 置 0:AI 网关流式响应无固定上界,WebSocket 为 hijack 连接
// 本就不受 net/http 超时管控;读侧超时已覆盖慢请求风险。
func serveHTTP(addr string, handler http.Handler) error {
	srv := &http.Server{
		Addr:              addr,
		Handler:           handler,
		ReadHeaderTimeout: 5 * time.Second,
		ReadTimeout:       60 * time.Second,
		IdleTimeout:       2 * time.Minute,
		MaxHeaderBytes:    64 << 10,
	}
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()
	errCh := make(chan error, 1)
	go func() { errCh <- srv.ListenAndServe() }()
	select {
	case err := <-errCh:
		return err
	case <-ctx.Done():
		log.Println("[info] shutting down http server")
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		return srv.Shutdown(shutdownCtx)
	}
}
