package router

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/atlasos/calypso/internal/common/cache"
	"github.com/gin-gonic/gin"
)

// GenerateKey generates a cache key from parts (local helper)
func GenerateKey(prefix string, parts ...string) string {
	key := prefix
	for _, part := range parts {
		key += ":" + part
	}

	// Hash long keys to keep them manageable
	if len(key) > 200 {
		hash := sha256.Sum256([]byte(key))
		return prefix + ":" + hex.EncodeToString(hash[:])
	}

	return key
}

// CacheConfig holds cache configuration
type CacheConfig struct {
	Enabled    bool
	DefaultTTL time.Duration
	MaxAge     int // seconds for Cache-Control header
}

// cacheMiddleware creates a caching middleware
func cacheMiddleware(cfg CacheConfig, cache *cache.Cache) gin.HandlerFunc {
	if !cfg.Enabled || cache == nil {
		return func(c *gin.Context) {
			c.Next()
		}
	}

	return func(c *gin.Context) {
		// Only cache GET requests
		if c.Request.Method != http.MethodGet {
			c.Next()
			return
		}

		// Don't cache VTL endpoints - they change frequently
		path := c.Request.URL.Path
		if strings.HasPrefix(path, "/api/v1/tape/vtl/") {
			c.Next()
			return
		}

		// Generate cache key from request path and query string
		keyParts := []string{c.Request.URL.Path}
		if c.Request.URL.RawQuery != "" {
			keyParts = append(keyParts, c.Request.URL.RawQuery)
		}
		cacheKey := GenerateKey("http", keyParts...)

		// Try to get from cache
		if cached, found := cache.Get(cacheKey); found {
			if cachedResponse, ok := cached.([]byte); ok {
				// Set cache headers
				if cfg.MaxAge > 0 {
					c.Header("Cache-Control", fmt.Sprintf("public, max-age=%d", cfg.MaxAge))
					c.Header("X-Cache", "HIT")
				}
				c.Data(http.StatusOK, "application/json", cachedResponse)
				c.Abort()
				return
			}
		}

		// Cache miss - capture response
		writer := &responseWriter{
			ResponseWriter: c.Writer,
			body:           &bytes.Buffer{},
		}
		c.Writer = writer

		c.Next()

		// Only cache successful responses
		if writer.Status() == http.StatusOK {
			// Cache the response body
			responseBody := writer.body.Bytes()
			cache.Set(cacheKey, responseBody)

			// Set cache headers
			if cfg.MaxAge > 0 {
				c.Header("Cache-Control", fmt.Sprintf("public, max-age=%d", cfg.MaxAge))
				c.Header("X-Cache", "MISS")
			}
		}
	}
}

// responseWriter wraps gin.ResponseWriter to capture response body
type responseWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w *responseWriter) Write(b []byte) (int, error) {
	w.body.Write(b)
	return w.ResponseWriter.Write(b)
}

func (w *responseWriter) WriteString(s string) (int, error) {
	w.body.WriteString(s)
	return w.ResponseWriter.WriteString(s)
}

// cacheControlMiddleware adds Cache-Control headers based on endpoint
func cacheControlMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path

		// Set appropriate cache control for different endpoints
		switch {
		case path == "/api/v1/health":
			// Health check can be cached for a short time
			c.Header("Cache-Control", "public, max-age=30")
		case path == "/api/v1/monitoring/metrics":
			// Metrics can be cached for a short time
			c.Header("Cache-Control", "public, max-age=60")
		case path == "/api/v1/monitoring/alerts":
			// Alerts should have minimal caching
			c.Header("Cache-Control", "public, max-age=10")
		case path == "/api/v1/storage/disks":
			// Disk list can be cached for a moderate time
			c.Header("Cache-Control", "public, max-age=300")
		case path == "/api/v1/storage/repositories":
			// Repositories can be cached
			c.Header("Cache-Control", "public, max-age=180")
		case path == "/api/v1/system/services":
			// Service list can be cached briefly
			c.Header("Cache-Control", "public, max-age=60")
		case strings.HasPrefix(path, "/api/v1/storage/zfs/pools"):
			// ZFS pools and datasets should not be cached - they change frequently
			c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
		default:
			// Default: no cache for other endpoints
			c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
		}

		c.Next()
	}
}

// InvalidateCacheKey invalidates a specific cache key
func InvalidateCacheKey(cache *cache.Cache, key string) {
	if cache != nil {
		cache.Delete(key)
	}
}

// InvalidateCachePattern invalidates all cache keys matching a pattern
func InvalidateCachePattern(cache *cache.Cache, pattern string) {
	if cache == nil {
		return
	}

	// Get all keys and delete matching ones
	// Note: This is a simple implementation. For production, consider using
	// a cache library that supports pattern matching (like Redis)
	stats := cache.Stats()
	if total, ok := stats["total_entries"].(int); ok && total > 0 {
		// For now, we'll clear the entire cache if pattern matching is needed
		// In production, use Redis with pattern matching
		cache.Clear()
	}
}
