// Package proxy 提供反向代理的核心功能，支持请求转发、负载均衡、健康检查等特性。
//
// 包含缓存处理器相关的结构体，用于配置代理缓存行为。
//
// 作者：xfy
package proxy

import (
	"time"

	"github.com/valyala/fasthttp"
	"rua.plus/lolly/internal/cache"
	"rua.plus/lolly/internal/loadbalance"
)

// buildCacheKeyHash 使用 FNV-64a 计算缓存键的 uint64 哈希值。
// 使用零分配方式构建哈希，避免 []byte(origKey) 转换。
func (p *Proxy) buildCacheKeyHash(ctx *fasthttp.RequestCtx) (uint64, string) {
	method := ctx.Request.Header.Method()
	uri := ctx.Request.URI().RequestURI()

	var h uint64 = 14695981039346656037
	for i := 0; i < len(method); i++ {
		h ^= uint64(method[i])
		h *= 1099511628211
	}
	h ^= uint64(':')
	h *= 1099511628211
	for i := 0; i < len(uri); i++ {
		h ^= uint64(uri[i])
		h *= 1099511628211
	}

	origKey := b2s(method) + ":" + b2s(uri)
	return h, origKey
}

func (p *Proxy) buildCacheKeyHashValue(ctx *fasthttp.RequestCtx) uint64 {
	method := ctx.Request.Header.Method()
	uri := ctx.Request.URI().RequestURI()

	var h uint64 = 14695981039346656037
	for i := 0; i < len(method); i++ {
		h ^= uint64(method[i])
		h *= 1099511628211
	}
	h ^= uint64(':')
	h *= 1099511628211
	for i := 0; i < len(uri); i++ {
		h ^= uint64(uri[i])
		h *= 1099511628211
	}
	return h
}

// writeCachedResponse 将缓存的响应写入 FastHTTP 响应上下文。
//
// 设置响应体、状态码、响应头，并添加 X-Cache: HIT 头标记缓存命中。
//
// 参数：
//   - ctx: FastHTTP 请求上下文
//   - entry: 缓存条目，包含响应数据和元数据
func (p *Proxy) writeCachedResponse(ctx *fasthttp.RequestCtx, entry *cache.ProxyCacheEntry) {
	ctx.Response.SetBody(entry.Data)
	ctx.Response.SetStatusCode(entry.Status)
	for key, value := range entry.Headers {
		ctx.Response.Header.Set(key, value)
	}
	ctx.Response.Header.Set("X-Cache", "HIT")
}

// backgroundRefresh 在后台异步刷新缓存条目。
//
// 向对应的上游目标发送请求，获取最新响应并更新缓存。
// 该方法在独立 goroutine 中运行，不阻塞主请求流程。
//
// 参数：
//   - req: 预复制的请求副本（调用方负责 Acquire/Release）
//   - target: 要刷新的后端目标
//   - hashKey: 缓存哈希键
//   - origKey: 缓存原始键
func (p *Proxy) backgroundRefresh(req *fasthttp.Request, target *loadbalance.Target, hashKey uint64, origKey string) {
	resp := fasthttp.AcquireResponse()
	defer fasthttp.ReleaseResponse(resp)

	// 如果启用 Revalidate，添加条件请求头
	if p.config.Cache.Revalidate {
		if entry, ok, _ := p.cache.Get(hashKey, origKey); ok {
			if entry.LastModified != "" {
				req.Header.Set("If-Modified-Since", entry.LastModified)
			}
			if entry.ETag != "" {
				req.Header.Set("If-None-Match", entry.ETag)
			}
		}
	}

	// 获取客户端
	client := p.getClient(target.URL)
	if client == nil {
		return
	}

	// 执行请求
	err := client.Do(req, resp)
	if err != nil {
		p.cache.ReleaseLock(hashKey, err)
		return
	}

	// 处理 304 Not Modified 响应
	if resp.StatusCode() == 304 {
		newHeaders := make(map[string]string, 5) // 预分配，通常只有 Last-Modified 和 ETag
		if lm := resp.Header.Peek("Last-Modified"); len(lm) > 0 {
			newHeaders["Last-Modified"] = b2s(lm)
		}
		if et := resp.Header.Peek("ETag"); len(et) > 0 {
			newHeaders["ETag"] = b2s(et)
		}
		p.cache.RefreshTTL(hashKey, origKey, newHeaders)
		return
	}

	// 提取响应头（使用 pool 复用 map）
	headers, ok := headersPool.Get().(map[string]string)
	if !ok {
		headers = make(map[string]string, 20)
	}
	for k := range headers {
		delete(headers, k)
	}
	for key, value := range resp.Header.All() {
		headers[string(key)] = string(value)
	}

	// 更新缓存
	p.cache.Set(hashKey, origKey, resp.Body(), headers, resp.StatusCode(), p.getCacheDuration(resp.StatusCode()))
}

// GetCache 返回代理的 ProxyCache 实例（用于 purge handler）。
// 如果缓存未启用，返回 nil。
func (p *Proxy) GetCache() *cache.ProxyCache {
	return p.cache
}

// GetCacheStats 返回代理缓存的统计信息。
// 如果缓存未启用，返回 nil。
func (p *Proxy) GetCacheStats() *cache.ProxyCacheStats {
	if p.cache == nil {
		return nil
	}
	stats := p.cache.Stats()
	return &stats
}

// getCacheDuration 根据状态码获取缓存时间。
// 优先级：CacheValid 配置 > MaxAge
//
// 映射规则：
//   - 200-299: CacheValid.OK（0 时继承 MaxAge）
//   - 301/302: CacheValid.Redirect
//   - 404: CacheValid.NotFound
//   - 400-499（除 404）: CacheValid.ClientError
//   - 500-599: CacheValid.ServerError
//   - 其他: 不缓存（返回 0）
func (p *Proxy) getCacheDuration(statusCode int) time.Duration {
	// 无 CacheValid 配置，使用 MaxAge
	if p.config.CacheValid == nil {
		return p.config.Cache.MaxAge
	}

	cv := p.config.CacheValid

	switch {
	case statusCode >= 200 && statusCode < 300:
		if cv.OK > 0 {
			return cv.OK
		}
		return p.config.Cache.MaxAge // 0 表示继承 MaxAge

	case statusCode == 301 || statusCode == 302:
		return cv.Redirect // 0 表示不缓存

	case statusCode == 404:
		return cv.NotFound

	case statusCode >= 400 && statusCode < 500:
		return cv.ClientError

	case statusCode >= 500:
		return cv.ServerError

	default:
		return 0 // 不缓存
	}
}
