package sqlite

import "strings"

// SQLiteToCanonicalTypes maps SQLite type names to canonical types.
// SQLite has type affinity rules; this maps common type names including
// MySQL/PostgreSQL types that users write in SQLite schemas.
var SQLiteToCanonicalTypes = map[string]string{
	// Integer affinity
	"integer":          "int",
	"int":              "int",
	"tinyint":          "int8",
	"smallint":         "int16",
	"mediumint":        "int",
	"bigint":           "int64",
	"unsigned big int": "uint64",
	"int2":             "int16",
	"int8":             "int64",
	// Real affinity
	"real":             "float64",
	"double":           "float64",
	"double precision": "float64",
	"float":            "float32",
	// Numeric affinity
	"numeric": "decimal",
	"decimal": "decimal",
	// Boolean (stored as integer in SQLite)
	"boolean": "bool",
	"bool":    "bool",
	// Date/time (stored as text in SQLite)
	"date":      "date",
	"datetime":  "timestamp",
	"timestamp": "timestamp",
	// Text affinity
	"text":              "string",
	"varchar":           "string",
	"char":              "string",
	"character":         "string",
	"varying character": "string",
	"nchar":             "string",
	"nvarchar":          "string",
	"clob":              "text",
	// Blob affinity
	"blob": "bytea",
}

// CanonicalToSQLiteAffinity maps type names to SQLite type affinity names.
// Accepts both Go canonical names ("int", "string") and SQL canonical names
// ("integer", "varchar") so the writer handles input from any reader.
// The five SQLite type affinities are TEXT, INTEGER, REAL, NUMERIC, BLOB.
var CanonicalToSQLiteAffinity = map[string]string{
	// INTEGER affinity — Go canonical
	"int":    "INTEGER",
	"int8":   "INTEGER",
	"int16":  "INTEGER",
	"int32":  "INTEGER",
	"int64":  "INTEGER",
	"uint":   "INTEGER",
	"uint8":  "INTEGER",
	"uint16": "INTEGER",
	"uint32": "INTEGER",
	"uint64": "INTEGER",
	"bool":   "INTEGER",
	// INTEGER affinity — SQL canonical
	"integer":     "INTEGER",
	"smallint":    "INTEGER",
	"bigint":      "INTEGER",
	"serial":      "INTEGER",
	"smallserial": "INTEGER",
	"bigserial":   "INTEGER",
	"boolean":     "INTEGER",
	"tinyint":     "INTEGER",
	"mediumint":   "INTEGER",
	// REAL affinity — Go canonical
	"float32": "REAL",
	"float64": "REAL",
	// REAL affinity — SQL canonical
	"real":             "REAL",
	"float":            "REAL",
	"double":           "REAL",
	"double precision": "REAL",
	// NUMERIC affinity
	"decimal":    "NUMERIC",
	"numeric":    "NUMERIC",
	"money":      "NUMERIC",
	"smallmoney": "NUMERIC",
	// BLOB affinity
	"bytea": "BLOB",
	"blob":  "BLOB",
	// TEXT affinity — Go canonical
	"string": "TEXT",
	"text":   "TEXT",
	// TEXT affinity — SQL canonical
	"varchar":     "TEXT",
	"char":        "TEXT",
	"nvarchar":    "TEXT",
	"nchar":       "TEXT",
	"citext":      "TEXT",
	"date":        "TEXT",
	"time":        "TEXT",
	"timetz":      "TEXT",
	"timestamp":   "TEXT",
	"timestamptz": "TEXT",
	"datetime":    "TEXT",
	"uuid":        "TEXT",
	"json":        "TEXT",
	"jsonb":       "TEXT",
	"xml":         "TEXT",
	"inet":        "TEXT",
	"cidr":        "TEXT",
	"macaddr":     "TEXT",
}

// ConvertSQLiteToCanonical converts a SQLite type name to the canonical type.
// Strips dimension parameters (e.g. VARCHAR(255) → string) and handles
// SQLite's flexible affinity rules. Defaults to "string" for unknown types.
func ConvertSQLiteToCanonical(sqliteType string) string {
	base := strings.ToUpper(strings.TrimSpace(sqliteType))
	if idx := strings.Index(base, "("); idx >= 0 {
		base = strings.TrimSpace(base[:idx])
	}
	lower := strings.ToLower(base)

	if canonical, ok := SQLiteToCanonicalTypes[lower]; ok {
		return canonical
	}

	// Prefix match for types like "VARYING CHARACTER(255)"
	for key, canonical := range SQLiteToCanonicalTypes {
		if strings.HasPrefix(lower, key) {
			return canonical
		}
	}

	return "string"
}

// ConvertCanonicalToSQLite converts a canonical type (or any SQL type) to its
// SQLite type affinity. Defaults to TEXT for unrecognised types.
func ConvertCanonicalToSQLite(canonicalType string) string {
	normalized := strings.ToLower(strings.TrimSpace(canonicalType))
	if idx := strings.Index(normalized, "("); idx >= 0 {
		normalized = strings.TrimSpace(normalized[:idx])
	}
	normalized = strings.TrimSuffix(normalized, "[]")

	if affinity, ok := CanonicalToSQLiteAffinity[normalized]; ok {
		return affinity
	}

	return "TEXT"
}
