package cassandra

import (
	"fmt"
	"reflect"
	"sync"
	"unicode"

	"github.com/scylladb/gocqlx/v2/table"
)

var (
	// metadataCache 快取已生成的 Metadata，避免重複反射解析
	// key: tableName + ":" + structType (不包含 keyspace，因為同一個 struct 在不同 keyspace 結構相同)
	metadataCache sync.Map
)

type cachedMetadata struct {
	columns  []string
	partKeys []string
	sortKeys []string
	err      error
}

// generateMetadata 根據傳入的 struct 產生 table.Metadata
// 使用快取機制避免重複反射解析，提升效能
func generateMetadata[T Table](doc T, keyspace string) (table.Metadata, error) {
	// 取得型別資訊
	t := reflect.TypeOf(doc)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	// 取得表名稱
	tableName := doc.TableName()
	if tableName == "" {
		return table.Metadata{}, ErrMissingTableName
	}

	// 構建快取 key: tableName:structType (不包含 keyspace)
	cacheKey := fmt.Sprintf("%s:%s", tableName, t.String())

	// 檢查快取
	if cached, ok := metadataCache.Load(cacheKey); ok {
		cachedMeta := cached.(cachedMetadata)
		if cachedMeta.err != nil {
			return table.Metadata{}, cachedMeta.err
		}
		// 從快取構建 metadata，動態加上 keyspace
		meta := table.Metadata{
			Name:    fmt.Sprintf("%s.%s", keyspace, tableName),
			Columns: make([]string, len(cachedMeta.columns)),
			PartKey: make([]string, len(cachedMeta.partKeys)),
			SortKey: make([]string, len(cachedMeta.sortKeys)),
		}
		copy(meta.Columns, cachedMeta.columns)
		copy(meta.PartKey, cachedMeta.partKeys)
		copy(meta.SortKey, cachedMeta.sortKeys)
		return meta, nil
	}

	// 快取未命中，生成 metadata
	columns := make([]string, 0, t.NumField())
	partKeys := make([]string, 0, t.NumField())
	sortKeys := make([]string, 0, t.NumField())

	// 遍歷所有 exported 欄位
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		// 跳過 unexported 欄位
		if field.PkgPath != "" {
			continue
		}
		// 如果欄位有標記 db:"-" 則跳過
		if tag := field.Tag.Get(DBFiledName); tag == "-" {
			continue
		}
		// 取得欄位名稱
		colName := field.Tag.Get(DBFiledName)
		if colName == "" {
			colName = toSnakeCase(field.Name)
		}
		columns = append(columns, colName)
		// 若有 partition_key:"true" 標記，加入 PartKey
		if field.Tag.Get(Pk) == "true" {
			partKeys = append(partKeys, colName)
		}
		// 若有 clustering_key:"true" 標記，加入 SortKey
		if field.Tag.Get(ClusterKey) == "true" {
			sortKeys = append(sortKeys, colName)
		}
	}
	if len(partKeys) == 0 {
		err := ErrNoPartitionKey
		// 快取錯誤結果
		metadataCache.Store(cacheKey, cachedMetadata{err: err})
		return table.Metadata{}, err
	}

	// 快取成功結果（只存結構資訊，不包含 keyspace）
	cachedMeta := cachedMetadata{
		columns:  make([]string, len(columns)),
		partKeys: make([]string, len(partKeys)),
		sortKeys: make([]string, len(sortKeys)),
	}
	copy(cachedMeta.columns, columns)
	copy(cachedMeta.partKeys, partKeys)
	copy(cachedMeta.sortKeys, sortKeys)
	metadataCache.Store(cacheKey, cachedMeta)

	// 組合並返回 Metadata（包含 keyspace）
	meta := table.Metadata{
		Name:    fmt.Sprintf("%s.%s", keyspace, tableName),
		Columns: columns,
		PartKey: partKeys,
		SortKey: sortKeys,
	}

	return meta, nil
}

// toSnakeCase 將 CamelCase 字串轉換為 snake_case
func toSnakeCase(s string) string {
	var result []rune
	for i, r := range s {
		if unicode.IsUpper(r) {
			if i > 0 {
				result = append(result, '_')
			}
			result = append(result, unicode.ToLower(r))
		} else {
			result = append(result, r)
		}
	}
	return string(result)
}
