package sync

import (
	"GoNavi-Wails/internal/connection"
	"GoNavi-Wails/internal/db"
	"fmt"
	"strconv"
	"strings"
)

func buildTDengineToMySQLPlan(config SyncConfig, tableName string, sourceDB db.Database, targetDB db.Database) (SchemaMigrationPlan, []connection.ColumnDefinition, []connection.ColumnDefinition, error) {
	plan := SchemaMigrationPlan{}
	sourceType := resolveMigrationDBType(config.SourceConfig)
	targetType := resolveMigrationDBType(config.TargetConfig)
	plan.SourceSchema, plan.SourceTable = normalizeSchemaAndTable(sourceType, selectedSyncSourceDatabase(config), tableName)
	plan.TargetSchema, plan.TargetTable = normalizeSchemaAndTable(targetType, selectedSyncTargetDatabase(config), tableName)
	plan.SourceQueryTable = qualifiedNameForQuery(sourceType, plan.SourceSchema, plan.SourceTable, tableName)
	plan.TargetQueryTable = qualifiedNameForQuery(targetType, plan.TargetSchema, plan.TargetTable, tableName)
	plan.PlannedAction = "使用已有目标表导入"

	sourceCols, sourceExists, err := inspectTableColumns(sourceDB, plan.SourceSchema, plan.SourceTable)
	if err != nil {
		return plan, nil, nil, fmt.Errorf("获取源表字段失败: %w", err)
	}
	if !sourceExists {
		return plan, nil, nil, fmt.Errorf("源表不存在或无列定义: %s", tableName)
	}

	targetCols, targetExists, err := inspectTableColumns(targetDB, plan.TargetSchema, plan.TargetTable)
	if err != nil {
		return plan, sourceCols, nil, fmt.Errorf("获取目标表字段失败: %w", err)
	}
	plan.TargetTableExists = targetExists
	plan.Warnings = append(plan.Warnings, tdengineSemanticWarnings(sourceCols)...)

	strategy := normalizeTargetTableStrategy(config.TargetTableStrategy)
	if targetExists {
		missing := diffMissingColumnNames(sourceCols, targetCols)
		if len(missing) > 0 {
			plan.Warnings = append(plan.Warnings, fmt.Sprintf("目标表缺失字段 %d 个：%s", len(missing), strings.Join(missing, ", ")))
		}
		if strategy != "existing_only" {
			plan.Warnings = append(plan.Warnings, "TDengine 源端当前不自动补齐已有目标表字段，请先确认目标表结构")
		}
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	}

	switch strategy {
	case "existing_only":
		plan.PlannedAction = "目标表不存在，需先手工创建"
		plan.Warnings = append(plan.Warnings, "当前策略要求目标表已存在，执行时不会自动建表")
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	case "smart", "auto_create_if_missing":
		plan.AutoCreate = true
		plan.PlannedAction = "目标表不存在，将自动建表后导入"
		createSQL, warnings, unsupported := buildTDengineToMySQLCreateTableSQL(plan.TargetQueryTable, sourceCols)
		plan.CreateTableSQL = createSQL
		plan.Warnings = append(plan.Warnings, warnings...)
		plan.UnsupportedObjects = append(plan.UnsupportedObjects, unsupported...)
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	default:
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	}
}

func buildTDengineToPGLikePlan(config SyncConfig, tableName string, sourceDB db.Database, targetDB db.Database) (SchemaMigrationPlan, []connection.ColumnDefinition, []connection.ColumnDefinition, error) {
	plan := SchemaMigrationPlan{}
	sourceType := resolveMigrationDBType(config.SourceConfig)
	targetType := resolveMigrationDBType(config.TargetConfig)
	plan.SourceSchema, plan.SourceTable = normalizeSchemaAndTable(sourceType, selectedSyncSourceDatabase(config), tableName)
	plan.TargetSchema, plan.TargetTable = normalizeSchemaAndTable(targetType, selectedSyncTargetDatabase(config), tableName)
	plan.SourceQueryTable = qualifiedNameForQuery(sourceType, plan.SourceSchema, plan.SourceTable, tableName)
	plan.TargetQueryTable = qualifiedNameForQuery(targetType, plan.TargetSchema, plan.TargetTable, tableName)
	plan.PlannedAction = "使用已有目标表导入"

	sourceCols, sourceExists, err := inspectTableColumns(sourceDB, plan.SourceSchema, plan.SourceTable)
	if err != nil {
		return plan, nil, nil, fmt.Errorf("获取源表字段失败: %w", err)
	}
	if !sourceExists {
		return plan, nil, nil, fmt.Errorf("源表不存在或无列定义: %s", tableName)
	}

	targetCols, targetExists, err := inspectTableColumns(targetDB, plan.TargetSchema, plan.TargetTable)
	if err != nil {
		return plan, sourceCols, nil, fmt.Errorf("获取目标表字段失败: %w", err)
	}
	plan.TargetTableExists = targetExists
	plan.Warnings = append(plan.Warnings, tdengineSemanticWarnings(sourceCols)...)

	strategy := normalizeTargetTableStrategy(config.TargetTableStrategy)
	if targetExists {
		missing := diffMissingColumnNames(sourceCols, targetCols)
		if len(missing) > 0 {
			plan.Warnings = append(plan.Warnings, fmt.Sprintf("目标表缺失字段 %d 个：%s", len(missing), strings.Join(missing, ", ")))
		}
		if strategy != "existing_only" {
			plan.Warnings = append(plan.Warnings, "TDengine 源端当前不自动补齐已有目标表字段，请先确认目标表结构")
		}
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	}

	switch strategy {
	case "existing_only":
		plan.PlannedAction = "目标表不存在，需先手工创建"
		plan.Warnings = append(plan.Warnings, "当前策略要求目标表已存在，执行时不会自动建表")
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	case "smart", "auto_create_if_missing":
		plan.AutoCreate = true
		plan.PlannedAction = "目标表不存在，将自动建表后导入"
		createSQL, warnings, unsupported := buildTDengineToPGLikeCreateTableSQL(targetType, plan.TargetQueryTable, sourceCols)
		plan.CreateTableSQL = createSQL
		plan.Warnings = append(plan.Warnings, warnings...)
		plan.UnsupportedObjects = append(plan.UnsupportedObjects, unsupported...)
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	default:
		return dedupeSchemaMigrationPlan(plan), sourceCols, targetCols, nil
	}
}

func buildTDengineToMySQLCreateTableSQL(targetQueryTable string, sourceCols []connection.ColumnDefinition) (string, []string, []string) {
	columnDefs := make([]string, 0, len(sourceCols))
	warnings := make([]string, 0)
	unsupported := []string{"TDengine 的索引/外键/触发器/超级表/TTL 等时序语义当前不会自动迁移"}
	for _, col := range sourceCols {
		def, colWarnings := buildTDengineToMySQLColumnDefinition(col)
		columnDefs = append(columnDefs, fmt.Sprintf("%s %s", quoteIdentByType("mysql", col.Name), def))
		warnings = append(warnings, colWarnings...)
	}
	createSQL := fmt.Sprintf("CREATE TABLE %s (\n  %s\n)", quoteQualifiedIdentByType("mysql", targetQueryTable), strings.Join(columnDefs, ",\n  "))
	return createSQL, dedupeStrings(warnings), dedupeStrings(unsupported)
}

func buildTDengineToPGLikeCreateTableSQL(targetType string, targetQueryTable string, sourceCols []connection.ColumnDefinition) (string, []string, []string) {
	columnDefs := make([]string, 0, len(sourceCols))
	warnings := make([]string, 0)
	unsupported := []string{"TDengine 的索引/外键/触发器/超级表/TTL 等时序语义当前不会自动迁移"}
	for _, col := range sourceCols {
		def, colWarnings := buildTDengineToPGLikeColumnDefinition(col)
		columnDefs = append(columnDefs, fmt.Sprintf("%s %s", quoteIdentByType(targetType, col.Name), def))
		warnings = append(warnings, colWarnings...)
	}
	createSQL := fmt.Sprintf("CREATE TABLE %s (\n  %s\n)", quoteQualifiedIdentByType(targetType, targetQueryTable), strings.Join(columnDefs, ",\n  "))
	return createSQL, dedupeStrings(warnings), dedupeStrings(unsupported)
}

func buildTDengineToMySQLColumnDefinition(col connection.ColumnDefinition) (string, []string) {
	targetType, warnings := mapTDengineColumnToMySQL(col)
	parts := []string{targetType}
	if strings.EqualFold(strings.TrimSpace(col.Nullable), "NO") {
		parts = append(parts, "NOT NULL")
	} else {
		parts = append(parts, "NULL")
	}
	return strings.Join(parts, " "), warnings
}

func buildTDengineToPGLikeColumnDefinition(col connection.ColumnDefinition) (string, []string) {
	targetType, warnings := mapTDengineColumnToPGLike(col)
	parts := []string{targetType}
	if strings.EqualFold(strings.TrimSpace(col.Nullable), "NO") {
		parts = append(parts, "NOT NULL")
	} else {
		parts = append(parts, "NULL")
	}
	return strings.Join(parts, " "), warnings
}

func tdengineSemanticWarnings(sourceCols []connection.ColumnDefinition) []string {
	warnings := []string{"TDengine 到关系型目标库当前仅迁移列与数据；超级表、TAG 关联、保留策略等时序语义会降级或丢失"}
	for _, col := range sourceCols {
		if isTDengineTagColumn(col) {
			warnings = append(warnings, fmt.Sprintf("字段 %s 为 TDengine TAG 列，迁移到关系型目标后将降级为普通字段", col.Name))
		}
	}
	return dedupeStrings(warnings)
}

func isTDengineTagColumn(col connection.ColumnDefinition) bool {
	return strings.EqualFold(strings.TrimSpace(col.Key), "TAG") || strings.Contains(strings.ToUpper(strings.TrimSpace(col.Extra)), "TAG")
}

func parseTDengineType(raw string) (string, int) {
	cleaned := strings.TrimSpace(strings.ToUpper(raw))
	if cleaned == "" {
		return "", 0
	}
	base := cleaned
	length := 0
	if idx := strings.Index(base, "("); idx >= 0 {
		end := strings.Index(base[idx+1:], ")")
		if end >= 0 {
			lengthText := strings.TrimSpace(base[idx+1 : idx+1+end])
			if v, err := strconv.Atoi(lengthText); err == nil {
				length = v
			}
		}
		base = strings.TrimSpace(base[:idx])
	}
	return base, length
}

func mapTDengineColumnToMySQL(col connection.ColumnDefinition) (string, []string) {
	base, length := parseTDengineType(col.Type)
	warnings := make([]string, 0)
	if isTDengineTagColumn(col) {
		warnings = append(warnings, fmt.Sprintf("字段 %s 为 TDengine TAG 列，已按普通列映射", col.Name))
	}
	switch base {
	case "BOOL", "BOOLEAN":
		return "tinyint(1)", warnings
	case "TINYINT":
		return "tinyint", warnings
	case "UTINYINT":
		return "tinyint unsigned", warnings
	case "SMALLINT":
		return "smallint", warnings
	case "USMALLINT":
		return "smallint unsigned", warnings
	case "INT", "INTEGER":
		return "int", warnings
	case "UINT":
		return "int unsigned", warnings
	case "BIGINT":
		return "bigint", warnings
	case "UBIGINT":
		return "bigint unsigned", warnings
	case "FLOAT":
		return "float", warnings
	case "DOUBLE":
		return "double", warnings
	case "DECIMAL", "NUMERIC":
		if length > 0 {
			return strings.ToLower(strings.TrimSpace(col.Type)), warnings
		}
		return "decimal(38,10)", warnings
	case "TIMESTAMP":
		return "datetime", warnings
	case "DATE":
		return "date", warnings
	case "JSON":
		return "json", warnings
	case "BINARY", "NCHAR", "VARCHAR", "VARBINARY":
		if length > 0 && length <= 65535 {
			return fmt.Sprintf("varchar(%d)", length), warnings
		}
		return "text", warnings
	default:
		warnings = append(warnings, fmt.Sprintf("字段 %s 类型 %s 暂无专门 MySQL 映射，已降级为 text", col.Name, col.Type))
		return "text", warnings
	}
}

func mapTDengineColumnToPGLike(col connection.ColumnDefinition) (string, []string) {
	base, length := parseTDengineType(col.Type)
	warnings := make([]string, 0)
	if isTDengineTagColumn(col) {
		warnings = append(warnings, fmt.Sprintf("字段 %s 为 TDengine TAG 列，已按普通列映射", col.Name))
	}
	switch base {
	case "BOOL", "BOOLEAN":
		return "boolean", warnings
	case "TINYINT", "UTINYINT", "SMALLINT":
		return "smallint", warnings
	case "USMALLINT", "INT", "INTEGER":
		return "integer", warnings
	case "UINT", "BIGINT":
		return "bigint", warnings
	case "UBIGINT":
		warnings = append(warnings, fmt.Sprintf("字段 %s 类型 UBIGINT 已映射为 numeric(20,0) 以避免无符号溢出", col.Name))
		return "numeric(20,0)", warnings
	case "FLOAT":
		return "real", warnings
	case "DOUBLE":
		return "double precision", warnings
	case "DECIMAL", "NUMERIC":
		if length > 0 {
			return strings.ToLower(strings.TrimSpace(col.Type)), warnings
		}
		return "numeric(38,10)", warnings
	case "TIMESTAMP":
		return "timestamp", warnings
	case "DATE":
		return "date", warnings
	case "JSON":
		return "jsonb", warnings
	case "BINARY", "NCHAR", "VARCHAR", "VARBINARY":
		if length > 0 {
			return fmt.Sprintf("varchar(%d)", length), warnings
		}
		return "text", warnings
	default:
		warnings = append(warnings, fmt.Sprintf("字段 %s 类型 %s 暂无专门 PG-like 映射，已降级为 text", col.Name, col.Type))
		return "text", warnings
	}
}
