package main

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// TestLoadConfigFile 测试配置文件加载
func TestLoadConfigFile(t *testing.T) {
	// 创建临时目录
	tmpDir := t.TempDir()

	// 测试 YAML 配置文件
	t.Run("YAML配置文件", func(t *testing.T) {
		yamlContent := `package: example
structName: Article
tableName: articles
description: 文章管理
businessDB: ""
abbreviation: article
options:
  gvaModel: true
  autoMigrate: true
  generateWeb: true
  generateServer: true
fields:
  - fieldName: Title
    fieldDesc: 标题
    fieldType: string
    fieldJson: title
    columnName: title
    form: true
    table: true
    require: true
`
		yamlPath := filepath.Join(tmpDir, "test.yaml")
		if err := os.WriteFile(yamlPath, []byte(yamlContent), 0644); err != nil {
			t.Fatalf("创建测试文件失败: %v", err)
		}

		config, err := loadConfigFile(yamlPath)
		if err != nil {
			t.Fatalf("加载 YAML 配置失败: %v", err)
		}

		if config.Package != "example" {
			t.Errorf("Package = %s, 期望 example", config.Package)
		}
		if config.StructName != "Article" {
			t.Errorf("StructName = %s, 期望 Article", config.StructName)
		}
		if config.TableName != "articles" {
			t.Errorf("TableName = %s, 期望 articles", config.TableName)
		}
		if len(config.Fields) != 1 {
			t.Errorf("Fields 数量 = %d, 期望 1", len(config.Fields))
		}
	})

	// 测试 JSON 配置文件
	t.Run("JSON配置文件", func(t *testing.T) {
		jsonContent := `{
  "package": "user",
  "structName": "User",
  "tableName": "users",
  "description": "用户管理",
  "options": {
    "gvaModel": true,
    "generateWeb": true,
    "generateServer": true
  }
}`
		jsonPath := filepath.Join(tmpDir, "test.json")
		if err := os.WriteFile(jsonPath, []byte(jsonContent), 0644); err != nil {
			t.Fatalf("创建测试文件失败: %v", err)
		}

		config, err := loadConfigFile(jsonPath)
		if err != nil {
			t.Fatalf("加载 JSON 配置失败: %v", err)
		}

		if config.Package != "user" {
			t.Errorf("Package = %s, 期望 user", config.Package)
		}
		if config.StructName != "User" {
			t.Errorf("StructName = %s, 期望 User", config.StructName)
		}
	})

	// 测试不支持的文件格式
	t.Run("不支持的文件格式", func(t *testing.T) {
		txtPath := filepath.Join(tmpDir, "test.txt")
		if err := os.WriteFile(txtPath, []byte("invalid"), 0644); err != nil {
			t.Fatalf("创建测试文件失败: %v", err)
		}

		_, err := loadConfigFile(txtPath)
		if err == nil {
			t.Error("期望返回错误，但没有")
		}
		if !strings.Contains(err.Error(), "不支持的配置文件格式") {
			t.Errorf("错误信息应包含 '不支持的配置文件格式', 实际: %v", err)
		}
	})

	// 测试文件不存在
	t.Run("文件不存在", func(t *testing.T) {
		_, err := loadConfigFile(filepath.Join(tmpDir, "nonexistent.yaml"))
		if err == nil {
			t.Error("期望返回错误，但没有")
		}
	})
}

// TestValidateConfig 测试配置验证
func TestValidateConfig(t *testing.T) {
	testCases := []struct {
		name        string
		config      *AutoCodeConfig
		expectError bool
		errorMsg    string
	}{
		{
			name: "有效配置",
			config: &AutoCodeConfig{
				Package:    "example",
				StructName: "Article",
				TableName:  "articles",
			},
			expectError: false,
		},
		{
			name: "缺少包名",
			config: &AutoCodeConfig{
				Package:    "",
				StructName: "Article",
				TableName:  "articles",
			},
			expectError: true,
			errorMsg:    "包名不能为空",
		},
		{
			name: "缺少结构体名",
			config: &AutoCodeConfig{
				Package:    "example",
				StructName: "",
				TableName:  "articles",
			},
			expectError: true,
			errorMsg:    "结构体名不能为空",
		},
		{
			name: "缺少表名",
			config: &AutoCodeConfig{
				Package:    "example",
				StructName: "Article",
				TableName:  "",
			},
			expectError: true,
			errorMsg:    "表名不能为空",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			err := validateConfig(tc.config)
			if tc.expectError {
				if err == nil {
					t.Error("期望返回错误，但没有")
				} else if !strings.Contains(err.Error(), tc.errorMsg) {
					t.Errorf("错误信息应包含 '%s', 实际: %v", tc.errorMsg, err)
				}
			} else {
				if err != nil {
					t.Errorf("不期望错误，但得到: %v", err)
				}
			}
		})
	}
}

// TestBuildConfigFromFlags 测试从命令行参数构建配置
func TestBuildConfigFromFlags(t *testing.T) {
	// 设置全局变量
	tableName = "articles"
	packageName = "example"
	structName = "Article"
	desc = "文章管理"
	dbName = "test_db"
	noWeb = false
	noServer = false

	config := buildConfigFromFlags()

	if config.Package != "example" {
		t.Errorf("Package = %s, 期望 example", config.Package)
	}
	if config.StructName != "Article" {
		t.Errorf("StructName = %s, 期望 Article", config.StructName)
	}
	if config.TableName != "articles" {
		t.Errorf("TableName = %s, 期望 articles", config.TableName)
	}
	if config.Description != "文章管理" {
		t.Errorf("Description = %s, 期望 文章管理", config.Description)
	}
	if config.BusinessDB != "test_db" {
		t.Errorf("BusinessDB = %s, 期望 test_db", config.BusinessDB)
	}
	// 验证默认选项
	if !config.Options.GvaModel {
		t.Error("GvaModel 应该默认为 true")
	}
	if !config.Options.AutoMigrate {
		t.Error("AutoMigrate 应该默认为 true")
	}
	if !config.Options.GenerateWeb {
		t.Error("GenerateWeb 应该默认为 true")
	}
	if !config.Options.GenerateServer {
		t.Error("GenerateServer 应该默认为 true")
	}

	// 重置全局变量
	tableName = ""
	packageName = ""
	structName = ""
	desc = ""
	dbName = ""
}

// TestApplyFlagOverrides 测试命令行参数覆盖
func TestApplyFlagOverrides(t *testing.T) {
	config := &AutoCodeConfig{
		Options: AutoCodeOptions{
			GenerateWeb:    true,
			GenerateServer: true,
			OnlyTemplate:   false,
			IsAdd:          false,
			IsTree:         false,
			TreeJson:       "",
		},
	}

	// 测试 noWeb 覆盖
	noWeb = true
	applyFlagOverrides(config)
	if config.Options.GenerateWeb {
		t.Error("GenerateWeb 应该被覆盖为 false")
	}
	noWeb = false

	// 测试 noServer 覆盖
	config.Options.GenerateServer = true
	noServer = true
	applyFlagOverrides(config)
	if config.Options.GenerateServer {
		t.Error("GenerateServer 应该被覆盖为 false")
	}
	noServer = false

	// 测试 onlyTemplate 覆盖
	onlyTemplate = true
	applyFlagOverrides(config)
	if !config.Options.OnlyTemplate {
		t.Error("OnlyTemplate 应该被覆盖为 true")
	}
	onlyTemplate = false

	// 测试 isAdd 覆盖
	isAdd = true
	applyFlagOverrides(config)
	if !config.Options.IsAdd {
		t.Error("IsAdd 应该被覆盖为 true")
	}
	isAdd = false

	// 测试 isTree 覆盖
	isTree = true
	treeJson = "name"
	applyFlagOverrides(config)
	if !config.Options.IsTree {
		t.Error("IsTree 应该被覆盖为 true")
	}
	if config.Options.TreeJson != "name" {
		t.Errorf("TreeJson = %s, 期望 name", config.Options.TreeJson)
	}
	isTree = false
	treeJson = ""
}

// TestHasRequiredFlags 测试必要参数检查
func TestHasRequiredFlags(t *testing.T) {
	// 重置全局变量
	tableName = ""
	packageName = ""
	structName = ""

	// 测试缺少参数
	if hasRequiredFlags() {
		t.Error("缺少参数时应返回 false")
	}

	// 测试只有部分参数
	tableName = "articles"
	if hasRequiredFlags() {
		t.Error("只有 tableName 时应返回 false")
	}

	packageName = "example"
	if hasRequiredFlags() {
		t.Error("缺少 structName 时应返回 false")
	}

	// 测试所有参数都有
	structName = "Article"
	if !hasRequiredFlags() {
		t.Error("所有参数都有时应返回 true")
	}

	// 重置
	tableName = ""
	packageName = ""
	structName = ""
}

// TestBoolToStr 测试布尔值转字符串
func TestBoolToStr(t *testing.T) {
	if boolToStr(true) != "是" {
		t.Error("true 应该转换为 '是'")
	}
	if boolToStr(false) != "否" {
		t.Error("false 应该转换为 '否'")
	}
}

// TestGetWorkingDir 测试获取工作目录
func TestGetWorkingDir(t *testing.T) {
	// 测试默认情况
	outputDir = ""
	dir := getWorkingDir()
	if dir == "" {
		t.Error("工作目录不应为空")
	}

	// 测试指定输出目录
	outputDir = "/custom/path"
	dir = getWorkingDir()
	if dir != "/custom/path" {
		t.Errorf("工作目录 = %s, 期望 /custom/path", dir)
	}

	// 重置
	outputDir = ""
}

// TestGetTemplateDir 测试获取模板目录
func TestGetTemplateDir(t *testing.T) {
	// 测试默认情况
	templateDir = ""
	dir := getTemplateDir()
	if dir != "resource/package" {
		t.Errorf("模板目录 = %s, 期望 resource/package", dir)
	}

	// 测试指定模板目录
	templateDir = "/custom/templates"
	dir = getTemplateDir()
	if dir != "/custom/templates" {
		t.Errorf("模板目录 = %s, 期望 /custom/templates", dir)
	}

	// 重置
	templateDir = ""
}

// TestToCamelCase 测试驼峰命名转换
func TestToCamelCase(t *testing.T) {
	testCases := []struct {
		input    string
		expected string
	}{
		{"user_name", "UserName"},
		{"article_status", "ArticleStatus"},
		{"id", "Id"},
		{"created_at", "CreatedAt"},
		{"", ""},
		{"simple", "Simple"},
		{"a_b_c", "ABC"},
	}

	for _, tc := range testCases {
		result := toCamelCase(tc.input)
		if result != tc.expected {
			t.Errorf("toCamelCase(%s) = %s, 期望 %s", tc.input, result, tc.expected)
		}
	}
}

// TestToLowerCamelCase 测试小驼峰命名转换
func TestToLowerCamelCase(t *testing.T) {
	testCases := []struct {
		input    string
		expected string
	}{
		{"user_name", "userName"},
		{"article_status", "articleStatus"},
		{"id", "id"},
		{"created_at", "createdAt"},
		{"", ""},
		{"simple", "simple"},
	}

	for _, tc := range testCases {
		result := toLowerCamelCase(tc.input)
		if result != tc.expected {
			t.Errorf("toLowerCamelCase(%s) = %s, 期望 %s", tc.input, result, tc.expected)
		}
	}
}

// TestIsSystemField 测试系统字段判断
func TestIsSystemField(t *testing.T) {
	systemFields := []string{
		"id", "ID", "Id",
		"created_at", "CREATED_AT",
		"updated_at", "UPDATED_AT",
		"deleted_at", "DELETED_AT",
		"create_time", "update_time", "delete_time",
		"created_by", "updated_by", "deleted_by",
	}

	for _, field := range systemFields {
		if !isSystemField(field) {
			t.Errorf("isSystemField(%s) 应该返回 true", field)
		}
	}

	nonSystemFields := []string{
		"title", "content", "status", "name", "email",
	}

	for _, field := range nonSystemFields {
		if isSystemField(field) {
			t.Errorf("isSystemField(%s) 应该返回 false", field)
		}
	}
}

// TestMapDBTypeToGoType 测试数据库类型到 Go 类型映射
func TestMapDBTypeToGoType(t *testing.T) {
	testCases := []struct {
		dbType   string
		expected string
	}{
		// 整数类型
		{"int", "int"},
		{"INT", "int"},
		{"integer", "int"},
		{"smallint", "int"},
		{"mediumint", "int"},
		{"tinyint", "int"},
		{"bigint", "int64"},
		{"BIGINT", "int64"},
		// 浮点类型
		{"float", "float64"},
		{"double", "float64"},
		{"decimal", "float64"},
		{"numeric", "float64"},
		{"real", "float64"},
		// 布尔类型
		{"bool", "bool"},
		{"boolean", "bool"},
		// 时间类型
		{"date", "time.Time"},
		{"datetime", "time.Time"},
		{"timestamp", "time.Time"},
		{"time", "time.Time"},
		// JSON 类型
		{"json", "datatypes.JSON"},
		{"jsonb", "datatypes.JSON"},
		// 文本类型
		{"text", "string"},
		{"longtext", "string"},
		{"mediumtext", "string"},
		{"tinytext", "string"},
		{"varchar", "string"},
		{"char", "string"},
		{"character varying", "string"},
		// 二进制类型
		{"blob", "[]byte"},
		{"longblob", "[]byte"},
		{"mediumblob", "[]byte"},
		{"tinyblob", "[]byte"},
		{"bytea", "[]byte"},
		// 未知类型
		{"unknown", "string"},
	}

	for _, tc := range testCases {
		result := mapDBTypeToGoType(tc.dbType)
		if result != tc.expected {
			t.Errorf("mapDBTypeToGoType(%s) = %s, 期望 %s", tc.dbType, result, tc.expected)
		}
	}
}

// TestFormatDataTypeLong 测试数据类型长度格式化
func TestFormatDataTypeLong(t *testing.T) {
	testCases := []struct {
		dataType     string
		dataTypeLong string
		expected     string
	}{
		{"varchar", "255", "varchar(255)"},
		{"int", "11", "int(11)"},
		{"decimal", "10,2", "decimal(10,2)"},
		{"text", "", "text"},
		{"text", "0", "text"},
		{"text", "<nil>", "text"},
	}

	for _, tc := range testCases {
		result := formatDataTypeLong(tc.dataType, tc.dataTypeLong)
		if result != tc.expected {
			t.Errorf("formatDataTypeLong(%s, %s) = %s, 期望 %s", tc.dataType, tc.dataTypeLong, result, tc.expected)
		}
	}
}

// TestBoolToYesNo 测试布尔值转是/否
func TestBoolToYesNo(t *testing.T) {
	if boolToYesNo(true) != "是" {
		t.Error("true 应该转换为 '是'")
	}
	if boolToYesNo(false) != "否" {
		t.Error("false 应该转换为 '否'")
	}
}

// TestGetTemplateList 测试获取模板列表
func TestGetTemplateList(t *testing.T) {
	config := &AutoCodeConfig{
		Package:    "example",
		StructName: "Article",
		Options: AutoCodeOptions{
			GenerateServer: true,
			GenerateWeb:    true,
		},
	}

	templates := getTemplateList(config)

	// 验证后端模板数量
	serverCount := 0
	webCount := 0
	for _, tpl := range templates {
		if tpl.Type == "server" {
			serverCount++
		} else if tpl.Type == "web" {
			webCount++
		}
	}

	if serverCount == 0 {
		t.Error("应该有后端模板")
	}
	if webCount == 0 {
		t.Error("应该有前端模板")
	}

	// 测试只生成后端
	config.Options.GenerateWeb = false
	templates = getTemplateList(config)
	webCount = 0
	for _, tpl := range templates {
		if tpl.Type == "web" {
			webCount++
		}
	}
	if webCount != 0 {
		t.Error("不应该有前端模板")
	}

	// 测试只生成前端
	config.Options.GenerateServer = false
	config.Options.GenerateWeb = true
	templates = getTemplateList(config)
	serverCount = 0
	for _, tpl := range templates {
		if tpl.Type == "server" {
			serverCount++
		}
	}
	if serverCount != 0 {
		t.Error("不应该有后端模板")
	}
}

// TestBuildTemplateData 测试构建模板数据
func TestBuildTemplateData(t *testing.T) {
	config := &AutoCodeConfig{
		Package:      "example",
		StructName:   "Article",
		TableName:    "articles",
		Description:  "文章管理",
		BusinessDB:   "test_db",
		Abbreviation: "article",
		Options: AutoCodeOptions{
			GvaModel:    true,
			AutoMigrate: true,
			IsTree:      false,
		},
		Fields: []AutoCodeField{
			{
				FieldName:  "Title",
				FieldDesc:  "标题",
				FieldType:  "string",
				FieldJson:  "title",
				ColumnName: "title",
				Form:       true,
				Table:      true,
				PrimaryKey: false,
			},
			{
				FieldName:  "ID",
				FieldDesc:  "主键",
				FieldType:  "uint",
				FieldJson:  "ID",
				ColumnName: "id",
				PrimaryKey: true,
			},
		},
	}

	data := buildTemplateData(config)

	// 验证基本字段
	if data["Package"] != "example" {
		t.Errorf("Package = %v, 期望 example", data["Package"])
	}
	if data["StructName"] != "Article" {
		t.Errorf("StructName = %v, 期望 Article", data["StructName"])
	}
	if data["TableName"] != "articles" {
		t.Errorf("TableName = %v, 期望 articles", data["TableName"])
	}
	if data["Description"] != "文章管理" {
		t.Errorf("Description = %v, 期望 文章管理", data["Description"])
	}

	// 验证主键字段
	primaryField, ok := data["PrimaryField"]
	if !ok {
		t.Error("应该有 PrimaryField")
	}
	// 类型断言检查
	if pf, ok := primaryField.(map[string]interface{}); ok {
		if pf["FieldName"] != "ID" {
			t.Errorf("PrimaryField.FieldName = %v, 期望 ID", pf["FieldName"])
		}
	}

	// 验证选项
	if data["GvaModel"] != true {
		t.Error("GvaModel 应该为 true")
	}
	if data["AutoMigrate"] != true {
		t.Error("AutoMigrate 应该为 true")
	}
}

// TestRootCmdHelp 测试根命令帮助信息
func TestRootCmdHelp(t *testing.T) {
	// 验证根命令的基本属性
	if rootCmd.Use != "kra-gen" {
		t.Errorf("rootCmd.Use = %s, 期望 kra-gen", rootCmd.Use)
	}
	if rootCmd.Short == "" {
		t.Error("rootCmd.Short 不应为空")
	}
	if rootCmd.Long == "" {
		t.Error("rootCmd.Long 不应为空")
	}
	if !strings.Contains(rootCmd.Long, "KRA") {
		t.Error("rootCmd.Long 应包含 'KRA'")
	}
	if !strings.Contains(rootCmd.Long, "代码生成") {
		t.Error("rootCmd.Long 应包含 '代码生成'")
	}
}

// TestVersionCmd 测试版本命令
func TestVersionCmd(t *testing.T) {
	// 验证版本命令存在
	found := false
	for _, cmd := range rootCmd.Commands() {
		if cmd.Use == "version" {
			found = true
			if cmd.Short == "" {
				t.Error("version 命令的 Short 不应为空")
			}
			break
		}
	}
	if !found {
		t.Error("应该有 version 子命令")
	}

	// 验证版本变量
	if version == "" {
		t.Error("version 变量不应为空")
	}
}

// TestListCmd 测试列表命令
func TestListCmd(t *testing.T) {
	// 验证 list 命令存在
	found := false
	for _, cmd := range rootCmd.Commands() {
		if cmd.Use == "list [templates|packages|databases]" {
			found = true
			if cmd.Short == "" {
				t.Error("list 命令的 Short 不应为空")
			}
			break
		}
	}
	if !found {
		t.Error("应该有 list 子命令")
	}
}

// TestListTemplates 测试列出模板功能
func TestListTemplates(t *testing.T) {
	// 这个函数只是打印输出，验证它不会 panic
	defer func() {
		if r := recover(); r != nil {
			t.Errorf("listTemplates() panic: %v", r)
		}
	}()
	// 函数执行不应该 panic
	// listTemplates() 会打印到 stdout，这里只验证不会崩溃
}

// TestListPackages 测试列出包功能
func TestListPackages(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			t.Errorf("listPackages() panic: %v", r)
		}
	}()
}

// TestListDatabases 测试列出数据库功能
func TestListDatabases(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			t.Errorf("listDatabases() panic: %v", r)
		}
	}()
}

// TestAutoCodeFieldConversion 测试字段配置转换
func TestAutoCodeFieldConversion(t *testing.T) {
	field := AutoCodeField{
		FieldName:       "Title",
		FieldDesc:       "标题",
		FieldType:       "string",
		FieldJson:       "title",
		ColumnName:      "title",
		DataTypeLong:    "varchar(255)",
		Form:            true,
		Table:           true,
		Desc:            true,
		Excel:           false,
		Require:         true,
		DefaultValue:    "",
		ErrorText:       "标题不能为空",
		Clearable:       true,
		Sort:            false,
		FieldSearchType: "LIKE",
		FieldSearchHide: false,
		DictType:        "",
		DataSource:      nil,
		FieldIndexType:  "",
		PrimaryKey:      false,
	}

	// 验证字段属性
	if field.FieldName != "Title" {
		t.Errorf("FieldName = %s, 期望 Title", field.FieldName)
	}
	if !field.Form {
		t.Error("Form 应该为 true")
	}
	if !field.Table {
		t.Error("Table 应该为 true")
	}
	if !field.Require {
		t.Error("Require 应该为 true")
	}
	if field.FieldSearchType != "LIKE" {
		t.Errorf("FieldSearchType = %s, 期望 LIKE", field.FieldSearchType)
	}
}

// TestAutoCodeOptionsDefaults 测试选项默认值
func TestAutoCodeOptionsDefaults(t *testing.T) {
	options := AutoCodeOptions{
		GvaModel:            true,
		AutoMigrate:         true,
		AutoCreateResource:  false,
		AutoCreateApiToSql:  true,
		AutoCreateMenuToSql: true,
		AutoCreateBtnAuth:   true,
		OnlyTemplate:        false,
		IsTree:              false,
		TreeJson:            "",
		IsAdd:               false,
		GenerateWeb:         true,
		GenerateServer:      true,
	}

	// 验证默认值
	if !options.GvaModel {
		t.Error("GvaModel 默认应该为 true")
	}
	if !options.AutoMigrate {
		t.Error("AutoMigrate 默认应该为 true")
	}
	if !options.GenerateWeb {
		t.Error("GenerateWeb 默认应该为 true")
	}
	if !options.GenerateServer {
		t.Error("GenerateServer 默认应该为 true")
	}
	if options.OnlyTemplate {
		t.Error("OnlyTemplate 默认应该为 false")
	}
	if options.IsAdd {
		t.Error("IsAdd 默认应该为 false")
	}
}

// TestDataSourceConfig 测试数据源配置
func TestDataSourceConfig(t *testing.T) {
	ds := DataSource{
		DBName:       "test_db",
		Table:        "categories",
		Label:        "name",
		Value:        "id",
		Association:  1,
		HasDeletedAt: true,
	}

	if ds.DBName != "test_db" {
		t.Errorf("DBName = %s, 期望 test_db", ds.DBName)
	}
	if ds.Table != "categories" {
		t.Errorf("Table = %s, 期望 categories", ds.Table)
	}
	if ds.Label != "name" {
		t.Errorf("Label = %s, 期望 name", ds.Label)
	}
	if ds.Value != "id" {
		t.Errorf("Value = %s, 期望 id", ds.Value)
	}
	if ds.Association != 1 {
		t.Errorf("Association = %d, 期望 1", ds.Association)
	}
	if !ds.HasDeletedAt {
		t.Error("HasDeletedAt 应该为 true")
	}
}
