package install

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
	"verification-platform-backend/internal/config"
	"verification-platform-backend/internal/database"
	"verification-platform-backend/internal/model"
	"verification-platform-backend/pkg/response"

	"github.com/gin-gonic/gin"
	"github.com/glebarez/sqlite"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

func SetupRoutes(r *gin.Engine) {
	install := r.Group("/api/install")
	{
		install.GET("/status", handleGetInstallStatus)
		install.POST("/setup", handleSetup)
		install.POST("/test-db", handleTestDatabase)
	}
}

type InstallStatus struct {
	Installed   bool   `json:"installed"`
	DbType      string `json:"db_type"`
	HasAdmin    bool   `json:"has_admin"`
	ConfigExist bool   `json:"config_exist"`
}

func handleGetInstallStatus(c *gin.Context) {
	configExist := checkConfigFile()
	hasAdmin := checkAdminExists()
	installed := hasAdmin

	var dbType string
	if configExist {
		dbType = config.GetString("database.type")
		if dbType == "" {
			if strings.Contains(config.GetString("database.host"), "") && config.GetString("database.host") != "localhost" {
				dbType = "mysql"
			} else {
				dbType = "sqlite"
			}
		}
	}

	response.Success(c, InstallStatus{
		Installed:   installed,
		DbType:      dbType,
		HasAdmin:    hasAdmin,
		ConfigExist: configExist,
	})
}

type TestDBRequest struct {
	DbType   string `json:"db_type"`
	Host     string `json:"host"`
	Port     string `json:"port"`
	Name     string `json:"name"`
	Username string `json:"username"`
	Password string `json:"password"`
}

func handleTestDatabase(c *gin.Context) {
	var req TestDBRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, 400, "参数错误")
		return
	}

	if req.DbType == "sqlite" {
		dataDir := config.GetDataDir()
		dbPath := filepath.Join(dataDir, "verification_platform.db")
		testDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
			Logger: logger.Default.LogMode(logger.Silent),
		})
		if err != nil {
			response.Error(c, 400, "无法创建SQLite数据库: "+err.Error())
			return
		}
		sqlDB, _ := testDB.DB()
		sqlDB.Close()
		response.Success(c, gin.H{"message": "SQLite数据库连接成功"})
		return
	}

	if req.DbType == "mysql" {
		if req.Host == "" || req.Port == "" || req.Name == "" || req.Username == "" {
			response.Error(c, 400, "MySQL配置不完整")
			return
		}
		dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
			req.Username, req.Password, req.Host, req.Port, req.Name)
		testDB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
			Logger: logger.Default.LogMode(logger.Silent),
		})
		if err != nil {
			response.Error(c, 400, "MySQL连接失败: "+err.Error())
			return
		}
		sqlDB, _ := testDB.DB()
		sqlDB.Close()
		response.Success(c, gin.H{"message": "MySQL连接成功"})
		return
	}

	response.Error(c, 400, "不支持的数据库类型")
}

type SetupRequest struct {
	DbType      string `json:"db_type"`
	DbHost      string `json:"db_host"`
	DbPort      string `json:"db_port"`
	DbName      string `json:"db_name"`
	DbUsername  string `json:"db_username"`
	DbPassword  string `json:"db_password"`
	JwtSecret   string `json:"jwt_secret"`
	AdminUser   string `json:"admin_user"`
	AdminPass   string `json:"admin_pass"`
	AdminEmail  string `json:"admin_email"`
	UseRedis    bool   `json:"use_redis"`
	RedisHost   string `json:"redis_host"`
	RedisPort   string `json:"redis_port"`
	RedisPass   string `json:"redis_password"`
	RedisDb     int    `json:"redis_db"`
}

func handleSetup(c *gin.Context) {
	if checkAdminExists() {
		response.Error(c, 400, "系统已安装")
		return
	}

	var req SetupRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, 400, "参数错误")
		return
	}

	if req.AdminUser == "" || req.AdminPass == "" {
		response.Error(c, 400, "管理员账号和密码不能为空")
		return
	}

	if len(req.AdminPass) < 6 {
		response.Error(c, 400, "密码长度至少6位")
		return
	}

	jwtSecret := req.JwtSecret
	if jwtSecret == "" {
		jwtSecret = generateRandomSecret(32)
	}

	var newDB *gorm.DB
	var err error

	if req.DbType == "sqlite" {
		dataDir := config.GetDataDir()
		dbPath := filepath.Join(dataDir, "verification_platform.db")
		newDB, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
			Logger:                                   logger.Default.LogMode(logger.Silent),
			DisableForeignKeyConstraintWhenMigrating: true,
		})
		if err != nil {
			response.Error(c, 500, "SQLite数据库创建失败: "+err.Error())
			return
		}
	} else if req.DbType == "mysql" {
		if req.DbHost == "" || req.DbPort == "" || req.DbName == "" || req.DbUsername == "" {
			response.Error(c, 400, "MySQL配置不完整")
			return
		}
		dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
			req.DbUsername, req.DbPassword, req.DbHost, req.DbPort, req.DbName)
		newDB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
			Logger:                                   logger.Default.LogMode(logger.Silent),
			DisableForeignKeyConstraintWhenMigrating: true,
		})
		if err != nil {
			response.Error(c, 500, "MySQL连接失败: "+err.Error())
			return
		}
	} else {
		response.Error(c, 400, "不支持的数据库类型")
		return
	}

	if sqlDB, err := newDB.DB(); err == nil {
		sqlDB.SetMaxIdleConns(10)
		sqlDB.SetMaxOpenConns(100)
		sqlDB.SetConnMaxLifetime(time.Hour)
	}

	if err := runMigrations(newDB); err != nil {
		response.Error(c, 500, "数据库迁移失败: "+err.Error())
		return
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.AdminPass), bcrypt.DefaultCost)
	if err != nil {
		response.Error(c, 500, "密码加密失败")
		return
	}

	admin := model.User{
		Username: req.AdminUser,
		Password: string(hashedPassword),
		Email:    &req.AdminEmail,
		Role:     "admin",
		Status:   "active",
	}

	if err := newDB.Create(&admin).Error; err != nil {
		response.Error(c, 500, "创建管理员失败: "+err.Error())
		return
	}

	configContent := generateConfigFile(req.DbType, req, jwtSecret)
	if err := saveConfigFile(configContent); err != nil {
		log.Printf("Warning: failed to save config file: %v", err)
	}

	database.DB = newDB

	response.Success(c, gin.H{
		"message":    "安装成功",
		"jwt_secret": jwtSecret,
	})
}

func checkConfigFile() bool {
	dataDir := config.GetDataDir()
	configPath := filepath.Join(dataDir, "config.yaml")
	if _, err := os.Stat(configPath); err == nil {
		return true
	}
	paths := []string{"config.yaml", "./config/config.yaml"}
	for _, p := range paths {
		if _, err := os.Stat(p); err == nil {
			return true
		}
	}
	return false
}

func checkAdminExists() bool {
	if database.DB == nil {
		return false
	}
	var count int64
	database.DB.Model(&model.User{}).Where("role = ?", "admin").Count(&count)
	return count > 0
}

func generateRandomSecret(length int) string {
	bytes := make([]byte, length)
	rand.Read(bytes)
	return hex.EncodeToString(bytes)[:length]
}

func runMigrations(db *gorm.DB) error {
	return db.AutoMigrate(
		&model.User{},
		&model.AppUser{},
		&model.Application{},
		&model.CardType{},
		&model.Card{},
		&model.RechargeRecord{},
		&model.UserDevice{},
		&model.DeviceSession{},
		&model.Setting{},
		&model.ExtensionAPIKey{},
		&model.AgentApplication{},
		&model.Package{},
		&model.Order{},
	)
}

func generateConfigFile(dbType string, req SetupRequest, jwtSecret string) string {
	var content strings.Builder

	content.WriteString("# 应用配置\n")
	content.WriteString("app:\n")
	content.WriteString("  name: verification-platform\n")
	content.WriteString("  env: production\n")
	content.WriteString("  port: \"8080\"\n")
	content.WriteString(fmt.Sprintf("  jwt_secret: %s\n", jwtSecret))
	content.WriteString("  jwt_expire: 24h\n\n")

	content.WriteString("# 数据库配置\n")
	content.WriteString("database:\n")
	content.WriteString(fmt.Sprintf("  type: %s\n", dbType))

	if dbType == "mysql" {
		content.WriteString(fmt.Sprintf("  host: %s\n", req.DbHost))
		content.WriteString(fmt.Sprintf("  port: %s\n", req.DbPort))
		content.WriteString(fmt.Sprintf("  name: %s\n", req.DbName))
		content.WriteString(fmt.Sprintf("  username: %s\n", req.DbUsername))
		content.WriteString(fmt.Sprintf("  password: %s\n", req.DbPassword))
	} else {
		content.WriteString("  host: localhost\n")
		content.WriteString("  port: \"3306\"\n")
		content.WriteString("  name: verification_platform\n")
		content.WriteString("  username: root\n")
		content.WriteString("  password: \"\"\n")
	}
	content.WriteString("  charset: utf8mb4\n")
	content.WriteString("  max_idle_conns: 10\n")
	content.WriteString("  max_open_conns: 100\n\n")

	content.WriteString("# Redis配置\n")
	content.WriteString("redis:\n")
	if req.UseRedis {
		content.WriteString(fmt.Sprintf("  host: %s\n", req.RedisHost))
		content.WriteString(fmt.Sprintf("  port: %s\n", req.RedisPort))
		content.WriteString(fmt.Sprintf("  password: %s\n", req.RedisPass))
		content.WriteString(fmt.Sprintf("  db: %d\n", req.RedisDb))
	} else {
		content.WriteString("  host: localhost\n")
		content.WriteString("  port: \"6379\"\n")
		content.WriteString("  password: \"\"\n")
		content.WriteString("  db: 0\n")
	}

	content.WriteString("\n# 日志配置\n")
	content.WriteString("log:\n")
	content.WriteString("  level: info\n")
	content.WriteString("  format: json\n")

	return content.String()
}

func saveConfigFile(content string) error {
	dataDir := config.GetDataDir()
	configPath := filepath.Join(dataDir, "config.yaml")
	file, err := os.Create(configPath)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(content)
	return err
}
