package main

import (
	stdErrors "errors"
	"os"
	"os/signal"
	"reflect"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/bytedance/sonic"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/compress"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/gofiber/fiber/v2/middleware/requestid"
	"github.com/google/uuid"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/internal/bootstrap"
	"github.com/break/junhong_cmp_fiber/internal/gateway"
	internalMiddleware "github.com/break/junhong_cmp_fiber/internal/middleware"
	"github.com/break/junhong_cmp_fiber/internal/routes"
	"github.com/break/junhong_cmp_fiber/internal/service/verification"
	"github.com/break/junhong_cmp_fiber/pkg/auth"
	pkgbootstrap "github.com/break/junhong_cmp_fiber/pkg/bootstrap"
	"github.com/break/junhong_cmp_fiber/pkg/config"
	"github.com/break/junhong_cmp_fiber/pkg/database"
	"github.com/break/junhong_cmp_fiber/pkg/logger"
	"github.com/break/junhong_cmp_fiber/pkg/queue"
	"github.com/break/junhong_cmp_fiber/pkg/sms"
	"github.com/break/junhong_cmp_fiber/pkg/storage"
)

func main() {
	// 1. 初始化配置
	cfg := initConfig()

	// 2. 初始化目录
	if _, err := pkgbootstrap.EnsureDirectories(cfg, nil); err != nil {
		panic("初始化目录失败: " + err.Error())
	}

	// 3. 初始化日志
	appLogger := initLogger(cfg)

	defer func() {
		_ = logger.Sync()
	}()

	// 5. 初始化数据库
	db := initDatabase(cfg, appLogger)
	defer closeDatabase(db, appLogger)

	// 6. 初始化 Redis
	redisClient := initRedis(cfg, appLogger)
	defer closeRedis(redisClient, appLogger)

	// 7. 初始化队列客户端
	queueClient := initQueue(redisClient, appLogger)
	defer closeQueue(queueClient, appLogger)

	// 8. 初始化认证管理器
	jwtManager, tokenManager, verificationSvc := initAuthComponents(cfg, redisClient, appLogger)

	// 9. 初始化对象存储服务（可选）
	storageSvc := initStorage(cfg, appLogger)

	// 9. 初始化 Gateway 客户端（可选）
	gatewayClient := initGateway(cfg, appLogger)

	// 10. 初始化所有业务组件（通过 Bootstrap）
	result, err := bootstrap.Bootstrap(&bootstrap.Dependencies{
		DB:                  db,
		Redis:               redisClient,
		Logger:              appLogger,
		JWTManager:          jwtManager,
		TokenManager:        tokenManager,
		VerificationService: verificationSvc,
		QueueClient:         queueClient,
		StorageService:      storageSvc,
		GatewayClient:       gatewayClient,
	})
	if err != nil {
		appLogger.Fatal("初始化业务组件失败", zap.Error(err))
	}

	// 11. 创建 Fiber 应用
	app := createFiberApp(cfg, appLogger)

	// 12. 注册中间件
	initMiddleware(app, cfg, appLogger)

	// 13. 注册路由
	initRoutes(app, cfg, result, queueClient, db, redisClient, appLogger)

	// 14. 生成 OpenAPI 文档
	generateOpenAPIDocs("logs/openapi.yaml", appLogger)

	// 15. 启动服务器
	startServer(app, cfg, appLogger)
}

// initConfig 加载配置
func initConfig() *config.Config {
	cfg, err := config.Load()
	if err != nil {
		panic("加载配置失败: " + err.Error())
	}
	return cfg
}

// initLogger 初始化日志
func initLogger(cfg *config.Config) *zap.Logger {
	if err := logger.InitLoggers(
		cfg.Logging.Level,
		cfg.Logging.Development,
		logger.LogRotationConfig{
			Filename:   cfg.Logging.AppLog.Filename,
			MaxSize:    cfg.Logging.AppLog.MaxSize,
			MaxBackups: cfg.Logging.AppLog.MaxBackups,
			MaxAge:     cfg.Logging.AppLog.MaxAge,
			Compress:   cfg.Logging.AppLog.Compress,
		},
		logger.LogRotationConfig{
			Filename:   cfg.Logging.AccessLog.Filename,
			MaxSize:    cfg.Logging.AccessLog.MaxSize,
			MaxBackups: cfg.Logging.AccessLog.MaxBackups,
			MaxAge:     cfg.Logging.AccessLog.MaxAge,
			Compress:   cfg.Logging.AccessLog.Compress,
		},
	); err != nil {
		panic("初始化日志失败: " + err.Error())
	}

	appLogger := logger.GetAppLogger()
	appLogger.Info("应用程序启动中...", zap.String("address", cfg.Server.Address))
	return appLogger
}

// initDatabase 初始化数据库连接
func initDatabase(cfg *config.Config, appLogger *zap.Logger) *gorm.DB {
	db, err := database.InitPostgreSQL(&cfg.Database, appLogger)
	if err != nil {
		appLogger.Fatal("初始化 PostgreSQL 失败", zap.Error(err))
	}
	return db
}

// closeDatabase 关闭数据库连接
func closeDatabase(db *gorm.DB, appLogger *zap.Logger) {
	sqlDB, _ := db.DB()
	if sqlDB != nil {
		if err := sqlDB.Close(); err != nil {
			appLogger.Error("关闭 PostgreSQL 连接失败", zap.Error(err))
		}
	}
}

// initRedis 初始化 Redis 连接
func initRedis(cfg *config.Config, appLogger *zap.Logger) *redis.Client {
	redisAddr := cfg.Redis.Address + ":" + strconv.Itoa(cfg.Redis.Port)
	redisClient, err := database.NewRedisClient(database.RedisConfig{
		Address:      redisAddr,
		Password:     cfg.Redis.Password,
		DB:           cfg.Redis.DB,
		PoolSize:     cfg.Redis.PoolSize,
		MinIdleConns: cfg.Redis.MinIdleConns,
		DialTimeout:  cfg.Redis.DialTimeout,
		ReadTimeout:  cfg.Redis.ReadTimeout,
		WriteTimeout: cfg.Redis.WriteTimeout,
	}, appLogger)

	if err != nil {
		appLogger.Fatal("连接 Redis 失败", zap.Error(err))
	}
	return redisClient
}

// closeRedis 关闭 Redis 连接
func closeRedis(redisClient *redis.Client, appLogger *zap.Logger) {
	if err := redisClient.Close(); err != nil {
		appLogger.Error("关闭 Redis 客户端失败", zap.Error(err))
	}
}

// initQueue 初始化队列客户端
func initQueue(redisClient *redis.Client, appLogger *zap.Logger) *queue.Client {
	return queue.NewClient(redisClient, appLogger)
}

// closeQueue 关闭队列客户端
func closeQueue(queueClient *queue.Client, appLogger *zap.Logger) {
	if err := queueClient.Close(); err != nil {
		appLogger.Error("关闭 Asynq 客户端失败", zap.Error(err))
	}
}

// createFiberApp 创建 Fiber 应用
func createFiberApp(cfg *config.Config, appLogger *zap.Logger) *fiber.App {
	registerTimeParserCompat()

	return fiber.New(fiber.Config{
		AppName:                  "君鸿卡管系统 v1.0.0",
		StrictRouting:            true,
		CaseSensitive:            true,
		JSONEncoder:              sonic.Marshal,
		JSONDecoder:              sonic.Unmarshal,
		EnableSplittingOnParsers: true,
		Prefork:                  cfg.Server.Prefork,
		ReadTimeout:              cfg.Server.ReadTimeout,
		WriteTimeout:             cfg.Server.WriteTimeout,
		ErrorHandler:             internalMiddleware.ErrorHandler(appLogger),
	})
}

// registerTimeParserCompat 注册时间参数解析兼容器
// 兼容格式：RFC3339、2006-01-02、2006-01-02 15:04:05、2006-01-02T15:04:05
func registerTimeParserCompat() {
	fiber.SetParserDecoder(fiber.ParserConfig{
		IgnoreUnknownKeys: true,
		ZeroEmpty:         true,
		ParserType: []fiber.ParserType{
			{
				Customtype: time.Time{},
				Converter: func(value string) reflect.Value {
					parsedTime, err := parseFlexibleTime(value)
					if err != nil {
						return reflect.Value{}
					}
					return reflect.ValueOf(parsedTime)
				},
			},
		},
	})
}

// parseFlexibleTime 解析多种常见时间格式
func parseFlexibleTime(value string) (time.Time, error) {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return time.Time{}, nil
	}

	// 优先支持带时区的标准时间格式。
	if parsed, err := time.Parse(time.RFC3339, trimmed); err == nil {
		return parsed, nil
	}

	// 兼容无时区格式，按服务器本地时区解析。
	layouts := []string{
		"2006-01-02 15:04:05",
		"2006-01-02T15:04:05",
		"2006-01-02",
	}
	var parseErr error
	for _, layout := range layouts {
		if parsed, err := time.ParseInLocation(layout, trimmed, time.Local); err == nil {
			return parsed, nil
		} else {
			parseErr = err
		}
	}

	if parseErr == nil {
		parseErr = stdErrors.New("时间格式不支持")
	}
	return time.Time{}, parseErr
}

// initMiddleware 注册中间件
func initMiddleware(app *fiber.App, cfg *config.Config, appLogger *zap.Logger) {
	// 1. Recover - 必须第一个，捕获所有 panic
	app.Use(internalMiddleware.Recover(appLogger))

	// 2. RequestID - 为每个请求生成唯一 ID
	app.Use(requestid.New(requestid.Config{
		Generator: func() string {
			return uuid.NewString()
		},
	}))

	// 3. Compress - 必须在 Logger 之前，否则 Logger 读到的是压缩后的乱码
	app.Use(compress.New(compress.Config{
		Level: compress.LevelDefault,
	}))

	// 4. Logger - 记录所有请求（在 Compress 之后注册，读到的是未压缩的原始响应体）
	app.Use(logger.Middleware())

	// 5. CORS - 必须在业务路由和认证中间件之前注册，确保预检请求不被认证拦截
	if cfg.Middleware.CORS.Enabled {
		app.Use(cors.New(cors.Config{
			AllowOrigins:     cfg.Middleware.CORS.AllowOrigins,
			AllowMethods:     cfg.Middleware.CORS.AllowMethods,
			AllowHeaders:     cfg.Middleware.CORS.AllowHeaders,
			ExposeHeaders:    cfg.Middleware.CORS.ExposeHeaders,
			AllowCredentials: cfg.Middleware.CORS.AllowCredentials,
			MaxAge:           cfg.Middleware.CORS.MaxAge,
		}))
		appLogger.Info("CORS 中间件已启用",
			zap.String("allow_origins", cfg.Middleware.CORS.AllowOrigins),
			zap.String("allow_methods", cfg.Middleware.CORS.AllowMethods),
			zap.Bool("allow_credentials", cfg.Middleware.CORS.AllowCredentials),
		)
	}
}

// initRoutes 注册路由
func initRoutes(app *fiber.App, cfg *config.Config, result *bootstrap.BootstrapResult, queueClient *queue.Client, db *gorm.DB, redisClient *redis.Client, appLogger *zap.Logger) {
	if cfg.Middleware.EnableRateLimiter {
		rateLimitMiddleware := createRateLimiter(cfg, appLogger)
		applyRateLimiterToBusinessRoutes(app, rateLimitMiddleware, appLogger)
	}

	routes.RegisterRoutes(app, result.Handlers, result.Middlewares)
}

// applyRateLimiterToBusinessRoutes 将限流器应用到真实业务路由组
func applyRateLimiterToBusinessRoutes(app *fiber.App, rateLimitMiddleware fiber.Handler, appLogger *zap.Logger) {
	adminGroup := app.Group("/api/admin")
	adminGroup.Use(rateLimitMiddleware)

	personalGroup := app.Group("/api/c/v1")
	personalGroup.Use(rateLimitMiddleware)

	appLogger.Info("限流器已应用到业务路由组",
		zap.Strings("paths", []string{"/api/admin", "/api/c/v1"}),
	)
}

// createRateLimiter 创建限流器中间件
func createRateLimiter(cfg *config.Config, appLogger *zap.Logger) fiber.Handler {
	var rateLimitStorage fiber.Storage

	if cfg.Middleware.RateLimiter.Storage == "redis" {
		rateLimitStorage = internalMiddleware.NewRedisStorage(
			cfg.Redis.Address,
			cfg.Redis.Password,
			cfg.Redis.DB,
			cfg.Redis.Port,
		)
		appLogger.Info("限流器使用 Redis 存储", zap.String("redis_address", cfg.Redis.Address))
	} else {
		rateLimitStorage = nil
		appLogger.Info("限流器使用内存存储")
	}

	return internalMiddleware.RateLimiter(
		cfg.Middleware.RateLimiter.Max,
		cfg.Middleware.RateLimiter.Expiration,
		rateLimitStorage,
	)
}

func startServer(app *fiber.App, cfg *config.Config, appLogger *zap.Logger) {
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, syscall.SIGTERM)

	go func() {
		if err := app.Listen(cfg.Server.Address); err != nil {
			appLogger.Fatal("服务器启动失败", zap.Error(err))
		}
	}()

	appLogger.Info("服务器已启动", zap.String("address", cfg.Server.Address))

	<-quit
	appLogger.Info("正在关闭服务器...")

	if err := app.ShutdownWithTimeout(cfg.Server.ShutdownTimeout); err != nil {
		appLogger.Error("强制关闭服务器", zap.Error(err))
	}

	appLogger.Info("服务器已停止")
}

func initAuthComponents(cfg *config.Config, redisClient *redis.Client, appLogger *zap.Logger) (*auth.JWTManager, *auth.TokenManager, *verification.Service) {
	jwtManager := auth.NewJWTManager(cfg.JWT.SecretKey, cfg.JWT.TokenDuration)

	accessTTL := time.Duration(cfg.JWT.AccessTokenTTL) * time.Second
	refreshTTL := time.Duration(cfg.JWT.RefreshTokenTTL) * time.Second
	tokenManager := auth.NewTokenManager(redisClient, accessTTL, refreshTTL)

	smsClient := initSMS(cfg, appLogger)
	verificationSvc := verification.NewService(redisClient, smsClient, appLogger)

	return jwtManager, tokenManager, verificationSvc
}

func initSMS(cfg *config.Config, appLogger *zap.Logger) *sms.Client {
	if cfg.SMS.GatewayURL == "" {
		appLogger.Info("短信服务未配置，跳过初始化")
		return nil
	}

	timeout := cfg.SMS.Timeout
	if timeout == 0 {
		timeout = 10 * time.Second
	}

	httpClient := sms.NewStandardHTTPClient(0)
	client := sms.NewClient(
		cfg.SMS.GatewayURL,
		cfg.SMS.Username,
		cfg.SMS.Password,
		cfg.SMS.Signature,
		timeout,
		appLogger,
		httpClient,
	)

	appLogger.Info("短信服务已初始化",
		zap.String("gateway_url", cfg.SMS.GatewayURL),
		zap.String("signature", cfg.SMS.Signature),
	)

	return client
}

func initStorage(cfg *config.Config, appLogger *zap.Logger) *storage.Service {
	if cfg.Storage.Provider == "" || cfg.Storage.S3.Endpoint == "" {
		appLogger.Info("对象存储未配置，跳过初始化")
		return nil
	}

	provider, err := storage.NewS3Provider(&cfg.Storage)
	if err != nil {
		appLogger.Warn("初始化对象存储失败，功能将不可用", zap.Error(err))
		return nil
	}

	appLogger.Info("对象存储已初始化",
		zap.String("provider", cfg.Storage.Provider),
		zap.String("bucket", cfg.Storage.S3.Bucket),
	)

	return storage.NewService(provider, &cfg.Storage)
}

func initGateway(cfg *config.Config, appLogger *zap.Logger) *gateway.Client {
	if cfg.Gateway.BaseURL == "" {
		appLogger.Info("Gateway 未配置，跳过初始化")
		return nil
	}

	client := gateway.NewClient(
		cfg.Gateway.BaseURL,
		cfg.Gateway.AppID,
		cfg.Gateway.AppSecret,
		appLogger,
	).WithTimeout(time.Duration(cfg.Gateway.Timeout) * time.Second)

	appLogger.Info("Gateway 客户端初始化成功",
		zap.String("base_url", cfg.Gateway.BaseURL),
		zap.String("app_id", cfg.Gateway.AppID))

	return client
}
