package admin

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/rose_cat707/luminary/internal/model"
	"github.com/rose_cat707/luminary/internal/provider"
	"github.com/rose_cat707/luminary/internal/store/cache"
)

func (h *Handler) ListEndpointMeta(c *gin.Context) {
	category := model.ProviderCategory(c.Query("category"))
	if category == "" {
		category = model.CategoryLLM
	}
	providerType := model.ProviderType(c.Query("type"))
	if providerType == "" {
		providerType = provider.DefaultProviderType(category)
	}
	endpoints := provider.EndpointsForCategory(category)
	providerTypes := provider.TypesForCategory(category)
	type item struct {
		provider.EndpointMeta
		DefaultPath string `json:"default_path"`
	}
	out := make([]item, 0, len(endpoints))
	for _, e := range endpoints {
		out = append(out, item{
			EndpointMeta: e,
			DefaultPath:  provider.DefaultPath(providerType, e.Key),
		})
	}
	c.JSON(http.StatusOK, gin.H{
		"categories": []gin.H{
			{"key": model.CategoryLLM, "label": provider.CategoryLabel(model.CategoryLLM), "supported": true},
			{"key": model.CategoryEmbedding, "label": provider.CategoryLabel(model.CategoryEmbedding), "supported": true},
			{"key": model.CategoryRerank, "label": provider.CategoryLabel(model.CategoryRerank), "supported": true},
			{"key": model.CategorySpeech, "label": provider.CategoryLabel(model.CategorySpeech), "supported": false},
			{"key": model.CategoryImage, "label": provider.CategoryLabel(model.CategoryImage), "supported": true},
			{"key": model.CategoryAudio, "label": provider.CategoryLabel(model.CategoryAudio), "supported": false},
		},
		"provider_types":     providerTypes,
		"all_provider_types": provider.AllProviderTypes(),
		"endpoints":          out,
	})
}

func (h *Handler) ListProviders(c *gin.Context) {
	category := c.Query("category")
	providers := h.cache.ListProviders()
	if category != "" {
		filtered := providers[:0]
		for _, p := range providers {
			if string(p.Category) == category {
				filtered = append(filtered, p)
			}
		}
		providers = filtered
	}
	out := make([]model.Provider, 0, len(providers))
	for _, p := range providers {
		p.Models = h.cache.GetModels(p.ID)
		out = append(out, p)
	}
	c.JSON(http.StatusOK, gin.H{"data": out})
}

func (h *Handler) CreateProvider(c *gin.Context) {
	var req struct {
		Name              string                 `json:"name"`
		Type              model.ProviderType     `json:"type"`
		Category          model.ProviderCategory `json:"category"`
		BaseURL           string                 `json:"base_url"`
		AllowedEndpoints  []string               `json:"allowed_endpoints"`
		EndpointPaths     map[string]string      `json:"endpoint_paths"`
	}
	if err := c.ShouldBindJSON(&req); err != nil || req.Name == "" || req.Type == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
		return
	}
	if req.Category == "" {
		req.Category = model.CategoryLLM
	}
	if !model.IsCategorySupported(req.Category) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "category not supported"})
		return
	}
	if !provider.IsTypeValidForCategory(req.Category, req.Type) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "protocol type not supported for this category"})
		return
	}
	if h.providerNameTaken(req.Name, 0) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "provider name already exists"})
		return
	}
	allowed := req.AllowedEndpoints
	if len(allowed) == 0 {
		allowed = provider.DefaultEndpointsForCategory(req.Category)
	}
	if err := provider.ValidateCategoryEndpoints(req.Category, allowed); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	pathsJSON, _ := json.Marshal(req.EndpointPaths)
	if req.EndpointPaths == nil {
		pathsJSON = []byte("{}")
	}
	p := model.Provider{
		Name:                 req.Name,
		Type:                 req.Type,
		Category:             req.Category,
		BaseURL:              req.BaseURL,
		AllowedEndpointsJSON: cache.ModelsToJSON(allowed),
		EndpointPathsJSON:    string(pathsJSON),
		Status:               model.ProviderUnknown,
	}
	if err := h.db.DB().Create(&p).Error; err != nil {
		if h.providerNameTaken(req.Name, 0) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "provider name already exists"})
			return
		}
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	h.cache.SetProvider(p)
	c.JSON(http.StatusCreated, p)
}

func (h *Handler) GetProvider(c *gin.Context) {
	id := parseUint(c.Param("id"))
	p, ok := h.cache.GetProvider(id)
	if !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}
	p.Keys = h.cache.ListProviderKeys(id)
	p.Models = h.cache.GetModels(id)
	c.JSON(http.StatusOK, p)
}

func (h *Handler) UpdateProvider(c *gin.Context) {
	id := parseUint(c.Param("id"))
	p, ok := h.cache.GetProvider(id)
	if !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}
	var req struct {
		Name             string                 `json:"name"`
		BaseURL          string                 `json:"base_url"`
		Category         model.ProviderCategory `json:"category"`
		AllowedEndpoints []string               `json:"allowed_endpoints"`
		EndpointPaths    map[string]string      `json:"endpoint_paths"`
		Enabled          *bool                  `json:"enabled"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
		return
	}
	if req.Name != "" && req.Name != p.Name {
		if h.providerNameTaken(req.Name, p.ID) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "provider name already exists"})
			return
		}
		p.Name = req.Name
	}
	if req.BaseURL != "" {
		p.BaseURL = req.BaseURL
	}
	if req.Category != "" {
		if !model.IsCategorySupported(req.Category) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "category not supported yet"})
			return
		}
		p.Category = req.Category
	}
	if req.AllowedEndpoints != nil {
		if err := provider.ValidateCategoryEndpoints(p.Category, req.AllowedEndpoints); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		p.AllowedEndpointsJSON = cache.ModelsToJSON(req.AllowedEndpoints)
	}
	if req.EndpointPaths != nil {
		b, _ := json.Marshal(req.EndpointPaths)
		p.EndpointPathsJSON = string(b)
	}
	if req.Enabled != nil {
		p.Enabled = *req.Enabled
	}
	h.db.DB().Save(&p)
	h.cache.SetProvider(p)
	c.JSON(http.StatusOK, p)
}
