package api

import (
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

const (
	// APIVersionHeader est le header HTTP pour spécifier la version de l'API
	APIVersionHeader = "X-API-Version"
	// AcceptHeaderVersion est le header Accept avec version (ex: application/vnd.veza.v1+json)
	AcceptHeaderVersion = "Accept"
	// DefaultAPIVersion est la version par défaut de l'API
	DefaultAPIVersion = "v1"
	// APIVersionKey est la clé utilisée pour stocker la version dans le contexte Gin
	APIVersionKey = "api_version"
)

// APIVersion représente une version de l'API
type APIVersion struct {
	Version     string // Ex: "v1", "v2"
	Deprecated  bool   // Indique si la version est dépréciée
	SunsetDate  string // Date de fin de support (RFC3339)
	Description string // Description de la version
}

// VersionManager gère les versions de l'API (BE-SVC-019)
type VersionManager struct {
	versions       map[string]*APIVersion
	defaultVersion string
	logger         *zap.Logger
}

// NewVersionManager crée un nouveau gestionnaire de versions
func NewVersionManager(logger *zap.Logger) *VersionManager {
	vm := &VersionManager{
		versions:       make(map[string]*APIVersion),
		defaultVersion: DefaultAPIVersion,
		logger:         logger,
	}

	// Enregistrer les versions par défaut
	vm.RegisterVersion(&APIVersion{
		Version:     "v1",
		Deprecated:  false,
		Description: "Current stable API version",
	})

	return vm
}

// RegisterVersion enregistre une nouvelle version de l'API
func (vm *VersionManager) RegisterVersion(version *APIVersion) {
	vm.versions[version.Version] = version
	vm.logger.Info("API version registered",
		zap.String("version", version.Version),
		zap.Bool("deprecated", version.Deprecated),
		zap.String("description", version.Description))
}

// GetVersion retourne les informations sur une version
func (vm *VersionManager) GetVersion(version string) (*APIVersion, bool) {
	v, exists := vm.versions[version]
	return v, exists
}

// GetDefaultVersion retourne la version par défaut
func (vm *VersionManager) GetDefaultVersion() string {
	return vm.defaultVersion
}

// SetDefaultVersion définit la version par défaut
func (vm *VersionManager) SetDefaultVersion(version string) {
	if _, exists := vm.versions[version]; exists {
		vm.defaultVersion = version
	}
}

// GetAllVersions retourne toutes les versions disponibles
func (vm *VersionManager) GetAllVersions() map[string]*APIVersion {
	result := make(map[string]*APIVersion)
	for k, v := range vm.versions {
		result[k] = v
	}
	return result
}

// VersionMiddleware est un middleware pour gérer le versioning de l'API (BE-SVC-019)
// Supporte plusieurs méthodes de spécification de version :
// 1. Header X-API-Version
// 2. Header Accept: application/vnd.veza.v1+json
// 3. URL path: /api/v1/...
func VersionMiddleware(versionManager *VersionManager) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Extraire la version depuis différentes sources
		version := extractAPIVersion(c)

		// Valider la version
		if version == "" {
			version = versionManager.GetDefaultVersion()
		}

		// Vérifier si la version existe
		apiVersion, exists := versionManager.GetVersion(version)
		if !exists {
			c.JSON(http.StatusBadRequest, gin.H{
				"error":              "Unsupported API version",
				"version":            version,
				"available_versions": getAvailableVersions(versionManager),
			})
			c.Abort()
			return
		}

		// Stocker la version dans le contexte
		c.Set(APIVersionKey, version)
		c.Set("api_version_info", apiVersion)

		// Ajouter des headers de réponse pour indiquer la version utilisée
		c.Header(APIVersionHeader, version)
		if apiVersion.Deprecated {
			c.Header("X-API-Deprecated", "true")
			c.Header("X-API-Version-Deprecated", "true")
			if apiVersion.SunsetDate != "" {
				c.Header("Sunset", apiVersion.SunsetDate)
			}
		}

		// Logger si version dépréciée
		if apiVersion.Deprecated {
			versionManager.logger.Warn("Deprecated API version used",
				zap.String("version", version),
				zap.String("path", c.Request.URL.Path),
				zap.String("sunset_date", apiVersion.SunsetDate))
		}

		c.Next()
	}
}

// extractAPIVersion extrait la version de l'API depuis différentes sources
func extractAPIVersion(c *gin.Context) string {
	// 1. Header X-API-Version
	if version := c.GetHeader(APIVersionHeader); version != "" {
		return normalizeVersion(version)
	}

	// 2. Header Accept: application/vnd.veza.v1+json
	if accept := c.GetHeader(AcceptHeaderVersion); accept != "" {
		if version := parseAcceptHeader(accept); version != "" {
			return version
		}
	}

	// 3. URL path: /api/v1/... ou /api/v2/...
	path := c.Request.URL.Path
	if strings.HasPrefix(path, "/api/") {
		parts := strings.Split(path, "/")
		if len(parts) >= 3 && strings.HasPrefix(parts[2], "v") {
			return normalizeVersion(parts[2])
		}
	}

	return ""
}

// normalizeVersion normalise une version (v1 -> v1, 1 -> v1, etc.)
func normalizeVersion(version string) string {
	version = strings.TrimSpace(version)
	version = strings.ToLower(version)
	if !strings.HasPrefix(version, "v") {
		version = "v" + version
	}
	return version
}

// parseAcceptHeader parse le header Accept pour extraire la version
// Format: application/vnd.veza.v1+json
func parseAcceptHeader(accept string) string {
	parts := strings.Split(accept, ",")
	for _, part := range parts {
		part = strings.TrimSpace(part)
		// Chercher le pattern vnd.veza.vX
		if strings.Contains(part, "vnd.veza.v") {
			start := strings.Index(part, "vnd.veza.v")
			if start != -1 {
				start += len("vnd.veza.v")
				end := start
				for end < len(part) && (part[end] >= '0' && part[end] <= '9') {
					end++
				}
				if end > start {
					return normalizeVersion("v" + part[start:end])
				}
			}
		}
	}
	return ""
}

// getAvailableVersions retourne la liste des versions disponibles
func getAvailableVersions(vm *VersionManager) []string {
	versions := make([]string, 0, len(vm.versions))
	for v := range vm.versions {
		versions = append(versions, v)
	}
	return versions
}

// GetAPIVersion retourne la version de l'API depuis le contexte Gin
func GetAPIVersion(c *gin.Context) string {
	if version, exists := c.Get(APIVersionKey); exists {
		if v, ok := version.(string); ok {
			return v
		}
	}
	return DefaultAPIVersion
}

// GetAPIVersionInfo retourne les informations complètes sur la version depuis le contexte
func GetAPIVersionInfo(c *gin.Context) *APIVersion {
	if info, exists := c.Get("api_version_info"); exists {
		if apiVersion, ok := info.(*APIVersion); ok {
			return apiVersion
		}
	}
	return nil
}

// VersionInfoHandler retourne les informations sur les versions disponibles
func VersionInfoHandler(versionManager *VersionManager) gin.HandlerFunc {
	return func(c *gin.Context) {
		versions := versionManager.GetAllVersions()

		response := gin.H{
			"current_version": versionManager.GetDefaultVersion(),
			"versions":        make(map[string]interface{}),
		}

		for version, info := range versions {
			versionInfo := gin.H{
				"version":     info.Version,
				"deprecated":  info.Deprecated,
				"description": info.Description,
			}
			if info.SunsetDate != "" {
				versionInfo["sunset_date"] = info.SunsetDate
			}
			response["versions"].(map[string]interface{})[version] = versionInfo
		}

		c.JSON(http.StatusOK, response)
	}
}
