// Package variable 提供高性能的变量系统，支持 nginx 风格的变量展开。
//
// 该包实现了统一的变量存储和展开机制，用于：
//   - 访问日志格式模板
//   - 代理请求头设置
//   - URL 重写规则
//
// 支持的变量格式：
//   - $var: 简单变量
//   - ${var}: 带花括号的变量（用于变量后有字符的场景）
//
// 性能特性：
//   - 使用快速字符串扫描（非正则表达式）
//   - sync.Pool 复用 VariableContext
//   - 内置变量惰性求值并缓存
//
// 作者：xfy
package variable

import (
	"maps"
	"strconv"
	"strings"
	"sync"

	"github.com/valyala/fasthttp"
)

// BuiltinVariable 内置变量定义
type BuiltinVariable struct {
	Getter      func(ctx *fasthttp.RequestCtx) string
	GetterBytes func(ctx *fasthttp.RequestCtx) []byte // 零拷贝 getter，用于 EphemeralGet
	Name        string
	Description string
}

// Context 变量上下文，绑定到请求
type Context struct {
	ctx                  *fasthttp.RequestCtx
	store                map[string]string
	cache                map[string]string // string 缓存（用于 PersistentGet）
	bytesCache           map[string][]byte // []byte 缓存（用于 EphemeralGet）
	serverName           string
	upstreamAddr         string
	status               int
	bodySize             int64
	duration             int64
	upstreamStatus       int
	upstreamResponseTime float64
	upstreamConnectTime  float64
	upstreamHeaderTime   float64
}

// pool 用于复用 Context
var pool = sync.Pool{
	New: func() any {
		return &Context{
			store:      make(map[string]string),
			cache:      make(map[string]string),
			bytesCache: make(map[string][]byte),
		}
	},
}

// 全局自定义变量存储
var (
	globalVariables     map[string]string
	globalVariablesLock sync.RWMutex
)

// SetGlobalVariables 设置全局自定义变量。
// 在应用启动或配置重载时调用。
func SetGlobalVariables(vars map[string]string) {
	globalVariablesLock.Lock()
	defer globalVariablesLock.Unlock()
	globalVariables = make(map[string]string, len(vars))
	maps.Copy(globalVariables, vars)
}

// GetGlobalVariable 获取全局变量值。
//
// 线程安全地查询全局自定义变量存储。
// 使用读锁保护，可在多个 goroutine 中并发调用。
//
// 参数：
//   - name: 变量名称
//
// 返回值：
//   - string: 变量值，不存在时返回空字符串
//   - bool: 变量是否存在，true 表示存在，false 表示不存在
func GetGlobalVariable(name string) (string, bool) {
	globalVariablesLock.RLock()
	defer globalVariablesLock.RUnlock()
	if globalVariables == nil {
		return "", false
	}
	v, ok := globalVariables[name]
	return v, ok
}

// builtinVars 内置变量注册表
var builtinVars = make(map[string]*BuiltinVariable)

// RegisterBuiltin 注册内置变量
func RegisterBuiltin(v *BuiltinVariable) {
	builtinVars[v.Name] = v
}

// GetBuiltin 获取内置变量定义
func GetBuiltin(name string) *BuiltinVariable {
	return builtinVars[name]
}

// NewContext 从池中获取 Context。
// 全局变量通过 Get() 惰性加载。
func NewContext(ctx *fasthttp.RequestCtx) *Context {
	vc, ok := pool.Get().(*Context)
	if !ok {
		// 池中类型不正确时返回新 Context
		return &Context{
			ctx:        ctx,
			store:      make(map[string]string),
			cache:      make(map[string]string),
			bytesCache: make(map[string][]byte),
		}
	}
	vc.ctx = ctx
	vc.status = 0
	vc.bodySize = 0
	vc.duration = 0
	vc.serverName = ""
	vc.upstreamAddr = ""
	vc.upstreamStatus = 0
	vc.upstreamResponseTime = 0
	vc.upstreamConnectTime = 0
	vc.upstreamHeaderTime = 0
	// 防御性初始化：防止从池中取出的 Context 因 map 为 nil 而 panic
	if vc.store == nil {
		vc.store = make(map[string]string)
	}
	if vc.cache == nil {
		vc.cache = make(map[string]string)
	}
	if vc.bytesCache == nil {
		vc.bytesCache = make(map[string][]byte)
	}
	// 清空内置变量缓存
	for k := range vc.cache {
		delete(vc.cache, k)
	}
	// 清空内置变量 bytes 缓存
	for k := range vc.bytesCache {
		delete(vc.bytesCache, k)
	}
	// 清空自定义变量 store
	for k := range vc.store {
		delete(vc.store, k)
	}
	return vc
}

// ReleaseContext 释放 Context 回池中
func ReleaseContext(vc *Context) {
	if vc == nil {
		return
	}
	vc.ctx = nil
	vc.status = 0
	vc.bodySize = 0
	vc.duration = 0
	vc.serverName = ""
	vc.upstreamAddr = ""
	vc.upstreamStatus = 0
	vc.upstreamResponseTime = 0
	vc.upstreamConnectTime = 0
	vc.upstreamHeaderTime = 0
	pool.Put(vc)
}

// SetResponseInfo 设置响应信息（用于需要 status、body_bytes_sent、request_time 的场景）
func (vc *Context) SetResponseInfo(status int, bodySize int64, durationNs int64) {
	vc.status = status
	vc.bodySize = bodySize
	vc.duration = durationNs
}

// SetServerName 设置服务器名称
func (vc *Context) SetServerName(name string) {
	vc.serverName = name
}

// SetUpstreamVars 设置上游变量
func (vc *Context) SetUpstreamVars(addr string, status int, responseTime, connectTime, headerTime float64) {
	vc.upstreamAddr = addr
	vc.upstreamStatus = status
	vc.upstreamResponseTime = responseTime
	vc.upstreamConnectTime = connectTime
	vc.upstreamHeaderTime = headerTime
}

// Get 获取变量值（优先自定义变量，再查全局变量，最后查内置变量）
func (vc *Context) Get(name string) (string, bool) {
	// 1. 先查自定义变量
	if v, ok := vc.store[name]; ok {
		return v, true
	}

	// 2. 惰性加载全局变量（首次访问时查找，避免每请求复制）
	if v, ok := GetGlobalVariable(name); ok {
		return v, true
	}

	// 3. 检查从 SetResponseInfo/SetServerName 设置的值
	// 优先检查 struct 字段，再检查 ctx.UserValue（兼容 SetResponseInfoInContext）
	switch name {
	case VarStatus:
		if vc.status > 0 {
			return strconv.Itoa(vc.status), true
		}
		if v := vc.ctx.UserValue(VarStatus); v != nil {
			if i, ok := v.(int); ok {
				return strconv.Itoa(i), true
			}
		}
	case VarBodyBytesSent:
		if vc.bodySize > 0 {
			return strconv.FormatInt(vc.bodySize, 10), true
		}
		if v := vc.ctx.UserValue(VarBodyBytesSent); v != nil {
			if i, ok := v.(int64); ok {
				return strconv.FormatInt(i, 10), true
			}
		}
		return "0", true
	case VarRequestTime:
		if vc.duration > 0 {
			// 转换为秒，保留 3 位小数
			seconds := float64(vc.duration) / 1e9
			return strconv.FormatFloat(seconds, 'f', 3, 64), true
		}
		if v := vc.ctx.UserValue(VarRequestTime); v != nil {
			if i, ok := v.(int64); ok {
				seconds := float64(i) / 1e9
				return strconv.FormatFloat(seconds, 'f', 3, 64), true
			}
		}
		return "0.000", true
	case VarServerName:
		if vc.serverName != "" {
			return vc.serverName, true
		}
	// 上游变量
	case VarUpstreamAddr:
		if vc.upstreamAddr != "" {
			return vc.upstreamAddr, true
		}
		return "-", true
	case VarUpstreamStatus:
		if vc.upstreamStatus > 0 {
			return strconv.Itoa(vc.upstreamStatus), true
		}
		return "-", true
	case VarUpstreamResponseTime:
		if vc.upstreamResponseTime > 0 {
			return strconv.FormatFloat(vc.upstreamResponseTime, 'f', 3, 64), true
		}
		return "-", true
	case VarUpstreamConnectTime:
		if vc.upstreamConnectTime > 0 {
			return strconv.FormatFloat(vc.upstreamConnectTime, 'f', 3, 64), true
		}
		return "-", true
	case VarUpstreamHeaderTime:
		if vc.upstreamHeaderTime > 0 {
			return strconv.FormatFloat(vc.upstreamHeaderTime, 'f', 3, 64), true
		}
		return "-", true
	}

	// 3. 查内置变量缓存
	if v, ok := vc.cache[name]; ok {
		return v, true
	}

	// 4. 求值内置变量并缓存
	if v, ok := vc.evalBuiltin(name); ok {
		vc.cache[name] = v
		return v, true
	}

	return "", false
}

// EphemeralGet 获取请求作用域内的变量值（返回 []byte）。
//
// 警告：返回的 []byte 在请求结束后失效。
// 安全用法：仅用于即时消费场景（如写入日志、响应头）。
// 如需持久化存储，请使用 PersistentGet()。
//
// 该方法通过 BuiltinVariable 中注册的 GetterBytes 函数
// 提供零拷贝访问变量值。
func (vc *Context) EphemeralGet(name string) []byte {
	// 1. 先查自定义变量（需要转换为 []byte）
	if v, ok := vc.store[name]; ok {
		return []byte(v) // 注意：这里分配了，因为 store 是 string
	}

	// 2. 查全局变量（需要转换为 []byte）
	if v, ok := GetGlobalVariable(name); ok {
		return []byte(v) // 注意：这里分配了，因为全局变量是 string
	}

	// 3. 检查 bytesCache 缓存
	if v, ok := vc.bytesCache[name]; ok {
		return v
	}

	// 4. 检查从 SetResponseInfo/SetServerName 设置的值
	// SAFETY: 这些值来自 struct 字段，在请求期间有效
	switch name {
	case VarStatus:
		if vc.status > 0 {
			b := []byte(strconv.Itoa(vc.status))
			vc.bytesCache[name] = b
			return b
		}
		if v := vc.ctx.UserValue(VarStatus); v != nil {
			if i, ok := v.(int); ok {
				b := []byte(strconv.Itoa(i))
				vc.bytesCache[name] = b
				return b
			}
		}
	case VarBodyBytesSent:
		if vc.bodySize > 0 {
			b := []byte(strconv.FormatInt(vc.bodySize, 10))
			vc.bytesCache[name] = b
			return b
		}
		if v := vc.ctx.UserValue(VarBodyBytesSent); v != nil {
			if i, ok := v.(int64); ok {
				b := []byte(strconv.FormatInt(i, 10))
				vc.bytesCache[name] = b
				return b
			}
		}
		b := []byte("0")
		vc.bytesCache[name] = b
		return b
	case VarRequestTime:
		if vc.duration > 0 {
			seconds := float64(vc.duration) / 1e9
			b := []byte(strconv.FormatFloat(seconds, 'f', 3, 64))
			vc.bytesCache[name] = b
			return b
		}
		if v := vc.ctx.UserValue(VarRequestTime); v != nil {
			if i, ok := v.(int64); ok {
				seconds := float64(i) / 1e9
				b := []byte(strconv.FormatFloat(seconds, 'f', 3, 64))
				vc.bytesCache[name] = b
				return b
			}
		}
		b := []byte("0.000")
		vc.bytesCache[name] = b
		return b
	case VarServerName:
		if vc.serverName != "" {
			return []byte(vc.serverName)
		}
	// 上游变量
	case VarUpstreamAddr:
		if vc.upstreamAddr != "" {
			return []byte(vc.upstreamAddr)
		}
		return []byte("-")
	case VarUpstreamStatus:
		if vc.upstreamStatus > 0 {
			b := []byte(strconv.Itoa(vc.upstreamStatus))
			vc.bytesCache[name] = b
			return b
		}
		return []byte("-")
	case VarUpstreamResponseTime:
		if vc.upstreamResponseTime > 0 {
			b := []byte(strconv.FormatFloat(vc.upstreamResponseTime, 'f', 3, 64))
			vc.bytesCache[name] = b
			return b
		}
		return []byte("-")
	case VarUpstreamConnectTime:
		if vc.upstreamConnectTime > 0 {
			b := []byte(strconv.FormatFloat(vc.upstreamConnectTime, 'f', 3, 64))
			vc.bytesCache[name] = b
			return b
		}
		return []byte("-")
	case VarUpstreamHeaderTime:
		if vc.upstreamHeaderTime > 0 {
			b := []byte(strconv.FormatFloat(vc.upstreamHeaderTime, 'f', 3, 64))
			vc.bytesCache[name] = b
			return b
		}
		return []byte("-")
	}

	// 5. 使用 GetterBytes 求值内置变量（零拷贝）
	if b, ok := vc.evalBuiltinBytes(name); ok {
		vc.bytesCache[name] = b
		return b
	}

	// 6. 如果只有 Getter，调用并转换为 []byte
	if v, ok := vc.evalBuiltin(name); ok {
		b := []byte(v)
		vc.bytesCache[name] = b
		return b
	}

	return nil
}

// PersistentGet 获取持久化字符串变量值。
//
// 当需要跨请求存储变量值时使用此方法
// （如保存到数据库、缓存或长期存活的结构体中）。
func (vc *Context) PersistentGet(name string) string {
	// 直接调用 Get，它返回 string
	v, _ := vc.Get(name)
	return v
}

// evalBuiltinBytes 求值内置变量，返回 []byte（零拷贝）
func (vc *Context) evalBuiltinBytes(name string) ([]byte, bool) {
	builtin := builtinVars[name]
	if builtin == nil || builtin.GetterBytes == nil {
		return nil, false
	}
	return builtin.GetterBytes(vc.ctx), true
}

// Set 设置自定义变量
func (vc *Context) Set(name string, value string) {
	vc.store[name] = value
}

// evalBuiltin 求值内置变量
func (vc *Context) evalBuiltin(name string) (string, bool) {
	builtin := builtinVars[name]
	if builtin == nil || builtin.Getter == nil {
		return "", false
	}
	return builtin.Getter(vc.ctx), true
}

// expandCore 是变量展开的核心实现
// lookup: 变量查找函数，返回变量值和是否找到
// keepOriginal: 当变量未找到时，是否保持原样（true=保持原样，false=替换为空字符串）
func expandCore(template string, lookup func(name string) (value string, found bool), keepOriginal bool) string {
	if template == "" {
		return ""
	}

	// 快速路径：没有变量
	hasVar := false
	for i := 0; i < len(template); i++ {
		if template[i] == '$' {
			hasVar = true
			break
		}
	}
	if !hasVar {
		return template
	}

	var result strings.Builder
	result.Grow(len(template) * 2)

	i := 0
	for i < len(template) {
		if template[i] != '$' {
			result.WriteByte(template[i])
			i++
			continue
		}

		// 到达末尾，保留 $
		if i+1 >= len(template) {
			result.WriteByte('$')
			i++
			continue
		}

		// ${var} 格式
		if template[i+1] == '{' {
			// 查找匹配的 }
			end := strings.IndexByte(template[i+2:], '}')
			if end == -1 {
				result.WriteByte('$')
				i++
				continue
			}
			// end 是相对 i+2 的偏移量
			varName := template[i+2 : i+2+end]
			if varName == "" {
				// 空变量名，保持 ${}
				result.WriteString("${}")
				i += 2 + end + 1
				continue
			}
			// 获取变量值
			if v, ok := lookup(varName); ok {
				result.WriteString(v)
			} else if keepOriginal {
				// 未定义变量，保持原样
				result.WriteString("${")
				result.WriteString(varName)
				result.WriteByte('}')
			}
			// i+2 是变量名开始，+end 是 } 的位置，+1 跳过 }
			i += 2 + end + 1
			continue
		}

		// $var 格式（变量名由字母、数字、下划线组成）
		j := i + 1
		for j < len(template) {
			c := template[j]
			if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' {
				j++
			} else {
				break
			}
		}

		if j == i+1 {
			// 变量名长度为0，保留 $
			result.WriteByte('$')
			i++
			continue
		}

		varName := template[i+1 : j]
		if v, ok := lookup(varName); ok {
			result.WriteString(v)
		} else if keepOriginal {
			// 未定义变量，保持原样
			result.WriteByte('$')
			result.WriteString(varName)
		}
		i = j // 跳过变量名
	}

	return result.String()
}

// Expand 展开模板字符串中的变量
// 支持 $var 和 ${var} 两种格式
// 对于未定义的变量，保持原样不变
func (vc *Context) Expand(template string) string {
	return expandCore(template, func(name string) (string, bool) {
		return vc.Get(name)
	}, true)
}
