// @title 萌芽小店 API
// @version 1.0.0-go
// @description 商品、下单、站点统计、收藏与客服聊天；用户登录由萌芽账户认证中心（SproutGate）校验。监听地址以 HTTP_LISTEN_ADDR 为准。
// @host localhost:8080
// @BasePath /
// @schemes http https

// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
// @description 用户访问令牌，格式: Bearer 空格 + token

// @securityDefinitions.apikey AdminToken
// @in header
// @name X-Admin-Token
// @description 管理端令牌（也可使用 Authorization 头或 query token，见各接口说明）

// @securityDefinitions.apikey WebhookSecret
// @in header
// @name X-Webhook-Secret
// @description 与服务端 WEBHOOK_MENGYA_SECRET 一致时校验萌芽支付 Webhook
package main

import (
	"context"
	"log"
	"log/slog"
	"net/http"
	"os"
	"time"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"

	_ "mengyastore-backend/docs"

	"mengyastore-backend/internal/auth"
	"mengyastore-backend/internal/config"
	"mengyastore-backend/internal/database"
	"mengyastore-backend/internal/handlers"
	"mengyastore-backend/internal/mq"
	"mengyastore-backend/internal/storage"
)

const apiVersion = "1.0.0-go"

func initLogging() {
	h := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
		Level: slog.LevelInfo,
		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
			if a.Key != slog.TimeKey {
				return a
			}
			t := a.Value.Time()
			if t.IsZero() {
				return a
			}
			return slog.String("time", t.Format("2006-01-02 15:04:05"))
		},
	})
	slog.SetDefault(slog.New(h))
}

func accessLog() gin.HandlerFunc {
	return func(c *gin.Context) {
		t0 := time.Now()
		path := c.Request.URL.Path
		q := c.Request.URL.RawQuery
		c.Next()
		p := path
		if q != "" {
			p = path + "?" + q
		}
		slog.Info("http",
			"method", c.Request.Method,
			"path", p,
			"status", c.Writer.Status(),
			"ms", time.Since(t0).Milliseconds(),
			"ip", c.ClientIP(),
		)
	}
}

// rootAPIInfo 浏览器或客户端访问服务根路径时返回 API 说明（JSON）。
// @Summary API 根信息与端点索引
// @Description 返回服务描述、主要路径指引与版本（非 OpenAPI 详尽列表，完整契约见 /swagger）。
// @Tags 元信息
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router / [get]
func rootAPIInfo(c *gin.Context) {
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	c.JSON(http.StatusOK, gin.H{
		"description": "萌芽小店电商后端：商品、下单、站点统计、收藏与客服聊天；用户登录认证由萌芽账户认证中心（SproutGate）校验。",
		"endpoints": gin.H{
			"health":     "/api/health",
			"public":     "/api/products, /api/checkout, /api/stats, /api/site/*, POST /api/products/:id/view",
			"orders":     "/api/orders (Bearer), GET /api/orders/:id/payment-status, POST /api/orders/:id/confirm",
			"webhooks":   "POST /api/webhooks/mengya-pay (萌芽支付到账，可选 X-Webhook-Secret)",
			"wishlist":   "/api/wishlist (Bearer)",
			"chat_user":  "/api/chat/messages (Bearer)",
			"chat_admin": "/api/admin/chat/* (X-Admin-Token)",
			"admin":      "/api/admin/* 含 verify、products、site、smtp、orders、system-status (X-Admin-Token)",
			"auth":       "用户认证 via 萌芽认证中心 (Authorization: Bearer)",
		},
		"message":   "萌芽小店 后端 API 服务运行中",
		"timestamp": time.Now().In(loc).Format(time.RFC3339),
		"version":   apiVersion,
	})
}

// HealthCheck 返回进程与可选 RabbitMQ 探活结果。
// @Summary 健康检查
// @Tags 健康检查
// @Produce json
// @Success 200 {object} map[string]interface{} "status ok；rabbitmq 为 disabled|ok|error:..."
// @Router /api/health [get]
func HealthCheck(mqClient *mq.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		resp := gin.H{"status": "ok", "rabbitmq": "disabled"}
		if mqClient != nil {
			if err := mqClient.Ping(); err != nil {
				resp["rabbitmq"] = "error: " + err.Error()
			} else {
				resp["rabbitmq"] = "ok"
			}
		}
		c.JSON(http.StatusOK, resp)
	}
}

func main() {
	initLogging()
	cfg, err := config.Load()
	if err != nil {
		log.Fatalf("加载配置失败: %v", err)
	}
	startedAt := time.Now()

	// 初始化数据库连接
	db, err := database.Open(cfg.DatabaseDSN)
	if err != nil {
		log.Fatalf("初始化数据库失败: %v", err)
	}

	store, err := storage.NewProductStore(db)
	if err != nil {
		log.Fatalf("初始化商品存储失败: %v", err)
	}
	orderStore, err := storage.NewOrderStore(db)
	if err != nil {
		log.Fatalf("初始化订单存储失败: %v", err)
	}
	siteStore, err := storage.NewSiteStore(db)
	if err != nil {
		log.Fatalf("初始化站点存储失败: %v", err)
	}
	wishlistStore, err := storage.NewWishlistStore(db)
	if err != nil {
		log.Fatalf("初始化收藏夹存储失败: %v", err)
	}
	chatStore, err := storage.NewChatStore(db)
	if err != nil {
		log.Fatalf("初始化聊天存储失败: %v", err)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	var mqClient *mq.Client
	if cfg.RabbitMQEnabled {
		if cfg.RabbitMQURL == "" {
			slog.Warn("mq", "event", "enabled_no_url", "hint", "set RABBITMQ_URL or RABBITMQ_PASSWORD")
		} else {
			c, err := mq.New(cfg.RabbitMQURL, cfg.RabbitMQEnv)
			if err != nil {
				slog.Warn("mq", "event", "connect_failed", "err", err)
			} else {
				mqClient = c
				defer mqClient.Close()
				go mqClient.StartConsumer(ctx, siteStore)
				slog.Info("mq", "event", "connected", "env", cfg.RabbitMQEnv, "exchange", mq.ExchangeName(cfg.RabbitMQEnv))
			}
		}
	}

	if cfg.GinDebug {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}
	r := gin.New()
	if err := r.SetTrustedProxies(nil); err != nil {
		slog.Warn("server", "event", "trusted_proxies", "err", err)
	}
	r.Use(gin.Recovery())
	r.Use(accessLog())
	r.Use(cors.New(cors.Config{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
		AllowHeaders:     []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Admin-Token", "X-Webhook-Secret"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: false,
		MaxAge:           12 * time.Hour,
	}))

	r.GET("/", rootAPIInfo)

	r.GET("/api/health", HealthCheck(mqClient))

	if cfg.EnableSwagger {
		r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
		slog.Info("server", "event", "swagger", "path", "/swagger/index.html")
	}

	authClient := auth.NewSproutGateClient(cfg.AuthAPIURL)

	publicHandler := handlers.NewPublicHandler(store)
	adminHandler := handlers.NewAdminHandler(store, cfg, siteStore, orderStore, chatStore)
	orderHandler := handlers.NewOrderHandler(store, orderStore, siteStore, authClient, mqClient, cfg.PaymentPendingTTLSecs, cfg.WebhookMengyaSecret)
	go orderHandler.RunExpiredPaymentSweep(ctx, 25*time.Second)
	statsHandler := handlers.NewStatsHandler(orderStore, siteStore)
	wishlistHandler := handlers.NewWishlistHandler(wishlistStore, authClient)
	chatHandler := handlers.NewChatHandler(chatStore, authClient)
	statusHandler := handlers.NewSystemStatusHandler(cfg, db, mqClient, startedAt)

	r.GET("/api/products", publicHandler.ListProducts)
	r.POST("/api/checkout", orderHandler.CreateOrder)
	r.GET("/api/orders/:id/payment-status", orderHandler.GetOrderPaymentStatus)
	r.POST("/api/webhooks/mengya-pay", orderHandler.MengyaPaymentWebhook)
	r.POST("/api/products/:id/view", publicHandler.RecordProductView)
	r.GET("/api/stats", statsHandler.GetStats)
	r.POST("/api/site/visit", statsHandler.RecordVisit)
	r.GET("/api/site/maintenance", statsHandler.GetMaintenance)
	r.GET("/api/orders", orderHandler.ListMyOrders)
	r.POST("/api/orders/:id/confirm", orderHandler.ConfirmOrder)
	r.POST("/api/orders/:id/cancel", orderHandler.CancelOrder)

	r.POST("/api/admin/verify", adminHandler.VerifyAdminToken)
	r.GET("/api/admin/products", adminHandler.ListAllProducts)
	r.POST("/api/admin/products", adminHandler.CreateProduct)
	r.PUT("/api/admin/products/:id", adminHandler.UpdateProduct)
	r.PATCH("/api/admin/products/:id/status", adminHandler.ToggleProduct)
	r.DELETE("/api/admin/products/:id", adminHandler.DeleteProduct)
	r.POST("/api/admin/site/maintenance", adminHandler.SetMaintenance)
	r.GET("/api/admin/site/smtp", adminHandler.GetSMTPConfig)
	r.POST("/api/admin/site/smtp", adminHandler.SetSMTPConfig)
	r.GET("/api/admin/orders", adminHandler.ListAllOrders)
	r.DELETE("/api/admin/orders/:id", adminHandler.DeleteOrder)
	r.GET("/api/admin/system-status", statusHandler.GetSystemStatus)

	r.GET("/api/wishlist", wishlistHandler.GetWishlist)
	r.POST("/api/wishlist", wishlistHandler.AddToWishlist)
	r.DELETE("/api/wishlist/:id", wishlistHandler.RemoveFromWishlist)

	// 用户聊天路由
	r.GET("/api/chat/messages", chatHandler.GetMyMessages)
	r.POST("/api/chat/messages", chatHandler.SendMyMessage)

	// 管理员聊天路由
	r.GET("/api/admin/chat", adminHandler.GetAllConversations)
	r.GET("/api/admin/chat/:account", adminHandler.GetConversation)
	r.POST("/api/admin/chat/:account", adminHandler.AdminReply)
	r.DELETE("/api/admin/chat/:account", adminHandler.ClearConversation)

	slog.Info("server", "event", "listen", "addr", cfg.HTTPListenAddr)
	if err := r.Run(cfg.HTTPListenAddr); err != nil {
		slog.Error("server", "event", "exit", "err", err)
		os.Exit(1)
	}
}
