package api

import (
	"net/http"
	"strconv"
	"strings"

	"github.com/ez-api/ez-api/internal/dto"
	"github.com/ez-api/ez-api/internal/model"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// CreateProviderGroup godoc
// @Summary      Create a provider group
// @Description  Create a provider group definition
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        group  body      dto.ProviderGroupDTO  true  "Provider group payload"
// @Success      201    {object}  model.ProviderGroup
// @Failure      400    {object}  gin.H
// @Failure      500    {object}  gin.H
// @Router       /admin/provider-groups [post]
func (h *Handler) CreateProviderGroup(c *gin.Context) {
	var req dto.ProviderGroupDTO
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	name := strings.TrimSpace(req.Name)
	if name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name required"})
		return
	}

	group := model.ProviderGroup{
		Name:           name,
		Type:           strings.TrimSpace(req.Type),
		BaseURL:        strings.TrimSpace(req.BaseURL),
		GoogleProject:  strings.TrimSpace(req.GoogleProject),
		GoogleLocation: strings.TrimSpace(req.GoogleLocation),
		StaticHeaders:  strings.TrimSpace(req.StaticHeaders),
		HeadersProfile: strings.TrimSpace(req.HeadersProfile),
		Models:         strings.Join(req.Models, ","),
		Status:         strings.TrimSpace(req.Status),
	}
	normalized, err := h.groupManager.NormalizeGroup(group)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := h.db.Create(&normalized).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create provider group", "details": err.Error()})
		return
	}

	if err := h.sync.SyncProvidersForGroup(h.db, normalized.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync providers", "details": err.Error()})
		return
	}
	if err := h.sync.SyncBindingsForGroup(h.db, normalized.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, normalized)
}

// ListProviderGroups godoc
// @Summary      List provider groups
// @Description  List all provider groups
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        page    query     int     false  "page (1-based)"
// @Param        limit   query     int     false  "limit (default 50, max 200)"
// @Param        search  query     string  false  "search by name/type"
// @Success      200     {array}   model.ProviderGroup
// @Failure      500     {object}  gin.H
// @Router       /admin/provider-groups [get]
func (h *Handler) ListProviderGroups(c *gin.Context) {
	var groups []model.ProviderGroup
	q := h.db.Model(&model.ProviderGroup{}).Order("id desc")
	query := parseListQuery(c)
	q = applyListSearch(q, query.Search, "name", "type")
	q = applyListPagination(q, query)
	if err := q.Find(&groups).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list provider groups", "details": err.Error()})
		return
	}
	c.JSON(http.StatusOK, groups)
}

// GetProviderGroup godoc
// @Summary      Get provider group
// @Description  Get a provider group by id
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        id   path      int  true  "ProviderGroup ID"
// @Success      200  {object}  model.ProviderGroup
// @Failure      400  {object}  gin.H
// @Failure      404  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/provider-groups/{id} [get]
func (h *Handler) GetProviderGroup(c *gin.Context) {
	id, ok := parseUintParam(c, "id")
	if !ok {
		return
	}
	var group model.ProviderGroup
	if err := h.db.First(&group, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider group not found"})
		return
	}
	c.JSON(http.StatusOK, group)
}

// UpdateProviderGroup godoc
// @Summary      Update provider group
// @Description  Update a provider group
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        id     path      int                  true  "ProviderGroup ID"
// @Param        group  body      dto.ProviderGroupDTO  true  "Provider group payload"
// @Success      200    {object}  model.ProviderGroup
// @Failure      400    {object}  gin.H
// @Failure      404    {object}  gin.H
// @Failure      500    {object}  gin.H
// @Router       /admin/provider-groups/{id} [put]
func (h *Handler) UpdateProviderGroup(c *gin.Context) {
	idParam := c.Param("id")
	id, err := strconv.Atoi(idParam)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
		return
	}

	var group model.ProviderGroup
	if err := h.db.First(&group, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider group not found"})
		return
	}

	var req dto.ProviderGroupDTO
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	next := group
	if strings.TrimSpace(req.Name) != "" {
		next.Name = strings.TrimSpace(req.Name)
	}
	if strings.TrimSpace(req.Type) != "" {
		next.Type = strings.TrimSpace(req.Type)
	}
	if strings.TrimSpace(req.BaseURL) != "" {
		next.BaseURL = strings.TrimSpace(req.BaseURL)
	}
	if strings.TrimSpace(req.GoogleProject) != "" {
		next.GoogleProject = strings.TrimSpace(req.GoogleProject)
	}
	if strings.TrimSpace(req.GoogleLocation) != "" {
		next.GoogleLocation = strings.TrimSpace(req.GoogleLocation)
	}
	if strings.TrimSpace(req.StaticHeaders) != "" {
		next.StaticHeaders = strings.TrimSpace(req.StaticHeaders)
	}
	if strings.TrimSpace(req.HeadersProfile) != "" {
		next.HeadersProfile = strings.TrimSpace(req.HeadersProfile)
	}
	if req.Models != nil {
		next.Models = strings.Join(req.Models, ",")
	}
	if strings.TrimSpace(req.Status) != "" {
		next.Status = strings.TrimSpace(req.Status)
	}

	normalized, err := h.groupManager.NormalizeGroup(next)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	group.Name = normalized.Name
	group.Type = normalized.Type
	group.BaseURL = normalized.BaseURL
	group.GoogleProject = normalized.GoogleProject
	group.GoogleLocation = normalized.GoogleLocation
	group.StaticHeaders = normalized.StaticHeaders
	group.HeadersProfile = normalized.HeadersProfile
	group.Models = normalized.Models
	group.Status = normalized.Status

	if err := h.db.Save(&group).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update provider group", "details": err.Error()})
		return
	}

	if err := h.sync.SyncProvidersForGroup(h.db, group.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync providers", "details": err.Error()})
		return
	}
	if err := h.sync.SyncBindingsForGroup(h.db, group.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
		return
	}

	c.JSON(http.StatusOK, group)
}

// DeleteProviderGroup godoc
// @Summary      Delete provider group
// @Description  Delete a provider group and its api keys/bindings
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        id   path      int  true  "ProviderGroup ID"
// @Success      200  {object}  gin.H
// @Failure      400  {object}  gin.H
// @Failure      404  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/provider-groups/{id} [delete]
func (h *Handler) DeleteProviderGroup(c *gin.Context) {
	id, ok := parseUintParam(c, "id")
	if !ok {
		return
	}
	var group model.ProviderGroup
	if err := h.db.First(&group, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider group not found"})
		return
	}

	if err := h.db.Transaction(func(tx *gorm.DB) error {
		if err := tx.Where("group_id = ?", group.ID).Delete(&model.APIKey{}).Error; err != nil {
			return err
		}
		if err := tx.Where("group_id = ?", group.ID).Delete(&model.Binding{}).Error; err != nil {
			return err
		}
		return tx.Delete(&group).Error
	}); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete provider group", "details": err.Error()})
		return
	}

	if err := h.sync.SyncProvidersForGroup(h.db, group.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync providers", "details": err.Error()})
		return
	}
	if err := h.sync.SyncBindingsForGroup(h.db, group.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "deleted"})
}
