package keyspace

import (
	"fmt"
	"strings"
	"unicode/utf8"
)

const maxMySQLIdentifierLength = 64

var systemKeyspaces = []string{
	"dse_security",
	"system",
	"system_accord",
	"system_auth",
	"system_cluster_metadata",
	"system_distributed",
	"system_schema",
	"system_traces",
}

var blockedKeyspaces = []string{
	"system_auth",
	"system_distributed",
}

var virtualKeyspaces = []string{
	"system_accord_debug",
	"system_accord_debug_remote",
	"system_metrics",
	"system_virtual_schema",
	"system_views",
}

var builtInKeyspaces = append(append([]string{}, systemKeyspaces...), virtualKeyspaces...)

var builtInKeyspaceSet = map[string]struct{}{}
var systemKeyspaceSet = map[string]struct{}{}
var virtualKeyspaceSet = map[string]struct{}{}
var blockedKeyspaceSet = map[string]struct{}{}

var internalSchemas = map[string]struct{}{
	"cql_meta":           {},
	"metrics_schema":     {},
	"mysql":              {},
	"performance_schema": {},
	"sys":                {},
}

func init() {
	for _, name := range systemKeyspaces {
		systemKeyspaceSet[name] = struct{}{}
		builtInKeyspaceSet[name] = struct{}{}
	}
	for _, name := range virtualKeyspaces {
		virtualKeyspaceSet[name] = struct{}{}
		builtInKeyspaceSet[name] = struct{}{}
	}
	for _, name := range blockedKeyspaces {
		blockedKeyspaceSet[name] = struct{}{}
	}
}

// BuiltInKeyspaces returns the built-in keyspace names (lowercase).
func BuiltInKeyspaces() []string {
	return builtInKeyspaces
}

// IsBuiltIn reports whether the keyspace is a built-in keyspace (case-insensitive).
func IsBuiltIn(name string) bool {
	if name == "" {
		return false
	}
	normalized := normalizeLowerASCII(name)
	_, ok := builtInKeyspaceSet[normalized]
	return ok
}

// IsSystem reports whether the keyspace is a built-in system keyspace (case-insensitive).
func IsSystem(name string) bool {
	if name == "" {
		return false
	}
	normalized := normalizeLowerASCII(name)
	_, ok := systemKeyspaceSet[normalized]
	return ok
}

// IsVirtual reports whether the keyspace is a built-in virtual keyspace (case-insensitive).
func IsVirtual(name string) bool {
	if name == "" {
		return false
	}
	normalized := normalizeLowerASCII(name)
	_, ok := virtualKeyspaceSet[normalized]
	return ok
}

// IsBlocked reports whether the keyspace is blocked for tenant access.
func IsBlocked(name string) bool {
	if name == "" {
		return false
	}
	normalized := normalizeLowerASCII(name)
	_, ok := blockedKeyspaceSet[normalized]
	return ok
}

// IsInternalSchema reports whether a schema is an internal MySQL schema (case-insensitive).
func IsInternalSchema(name string) bool {
	if name == "" {
		return false
	}
	normalized := normalizeLowerASCII(name)
	_, ok := internalSchemas[normalized]
	return ok
}

// ValidateIdentifier validates a MySQL identifier length and encoding.
func ValidateIdentifier(name, kind string) error {
	if name == "" {
		return fmt.Errorf("invalid %s: empty", kind)
	}
	if strings.IndexByte(name, 0) != -1 {
		return fmt.Errorf("invalid %s %q: contains NUL", kind, name)
	}
	if !utf8.ValidString(name) {
		return fmt.Errorf("invalid %s %q: invalid UTF-8", kind, name)
	}
	if len(name) > maxMySQLIdentifierLength {
		return fmt.Errorf("invalid %s %q: length exceeds %d", kind, name, maxMySQLIdentifierLength)
	}
	return nil
}

func normalizeLowerASCII(name string) string {
	for i := 0; i < len(name); i++ {
		ch := name[i]
		if ch >= 'A' && ch <= 'Z' {
			return strings.ToLower(name)
		}
	}
	return name
}
