package handlers

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

	"github.com/glueops/autoglue/internal/api/httpmiddleware"
	"github.com/glueops/autoglue/internal/common"
	"github.com/glueops/autoglue/internal/handlers/dto"
	"github.com/glueops/autoglue/internal/models"
	"github.com/glueops/autoglue/internal/utils"
	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// ListLabels godoc
//
//	@ID				ListLabels
//	@Summary		List node labels (org scoped)
//	@Description	Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups.
//	@Tags			Labels
//	@Produce		json
//	@Param			X-Org-ID	header		string	false	"Organization UUID"
//	@Param			key			query		string	false	"Exact key"
//	@Param			value		query		string	false	"Exact value"
//	@Param			q			query		string	false	"Key contains (case-insensitive)"
//	@Success		200			{array}		dto.LabelResponse
//	@Failure		401			{string}	string	"Unauthorized"
//	@Failure		403			{string}	string	"organization required"
//	@Failure		500			{string}	string	"failed to list node taints"
//	@Router			/labels [get]
//	@Security		BearerAuth
//	@Security		OrgKeyAuth
//	@Security		OrgSecretAuth
func ListLabels(db *gorm.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
		if !ok {
			utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
			return
		}

		q := db.Where("organization_id = ?", orgID)

		if key := strings.TrimSpace(r.URL.Query().Get("key")); key != "" {
			q = q.Where(`key = ?`, key)
		}
		if val := strings.TrimSpace(r.URL.Query().Get("value")); val != "" {
			q = q.Where(`value = ?`, val)
		}
		if needle := strings.TrimSpace(r.URL.Query().Get("q")); needle != "" {
			q = q.Where(`key ILIKE ?`, "%"+needle+"%")
		}
		var out []dto.LabelResponse
		if err := q.Model(&models.Label{}).Order("created_at DESC").Scan(&out).Error; err != nil {
			utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
			return
		}
		if out == nil {
			out = []dto.LabelResponse{}
		}

		utils.WriteJSON(w, http.StatusOK, out)
	}
}

// GetLabel godoc
//
//	@ID				GetLabel
//	@Summary		Get label by ID (org scoped)
//	@Description	Returns one label.
//	@Tags			Labels
//	@Produce		json
//	@Param			X-Org-ID	header		string	false	"Organization UUID"
//	@Param			id			path		string	true	"Label ID (UUID)"
//	@Success		200			{object}	dto.LabelResponse
//	@Failure		400			{string}	string	"invalid id"
//	@Failure		401			{string}	string	"Unauthorized"
//	@Failure		403			{string}	string	"organization required"
//	@Failure		404			{string}	string	"not found"
//	@Failure		500			{string}	string	"fetch failed"
//	@Router			/labels/{id} [get]
//	@Security		BearerAuth
//	@Security		OrgKeyAuth
//	@Security		OrgSecretAuth
func GetLabel(db *gorm.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
		if !ok {
			utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
			return
		}

		id, err := uuid.Parse(chi.URLParam(r, "id"))
		if err != nil {
			utils.WriteError(w, http.StatusBadRequest, "id_required", "id required")
			return
		}

		var out dto.LabelResponse
		if err := db.Model(&models.Label{}).Where("id = ? AND organization_id = ?", id, orgID).Limit(1).Scan(&out).Error; err != nil {
			if out.ID == uuid.Nil {
				utils.WriteError(w, http.StatusNotFound, "label_not_found", "label not found")
				return
			}
		}

		utils.WriteJSON(w, http.StatusOK, out)
	}
}

// CreateLabel godoc
//
//	@ID				CreateLabel
//	@Summary		Create label (org scoped)
//	@Description	Creates a label.
//	@Tags			Labels
//	@Accept			json
//	@Produce		json
//	@Param			X-Org-ID	header		string					false	"Organization UUID"
//	@Param			body		body		dto.CreateLabelRequest	true	"Label payload"
//	@Success		201			{object}	dto.LabelResponse
//	@Failure		400			{string}	string	"invalid json / missing fields / invalid node_pool_ids"
//	@Failure		401			{string}	string	"Unauthorized"
//	@Failure		403			{string}	string	"organization required"
//	@Failure		500			{string}	string	"create failed"
//	@Router			/labels [post]
//	@Security		BearerAuth
//	@Security		OrgKeyAuth
//	@Security		OrgSecretAuth
func CreateLabel(db *gorm.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
		if !ok {
			utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
			return
		}

		var req dto.CreateLabelRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			utils.WriteError(w, http.StatusBadRequest, "bad_request", "bad request")
			return
		}

		req.Key = strings.TrimSpace(req.Key)
		req.Value = strings.TrimSpace(req.Value)

		if req.Key == "" || req.Value == "" {
			utils.WriteError(w, http.StatusBadRequest, "bad_request", "missing key/value")
			return
		}

		l := models.Label{
			AuditFields: common.AuditFields{OrganizationID: orgID},
			Key:         req.Key,
			Value:       req.Value,
		}
		if err := db.Create(&l).Error; err != nil {
			utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
			return
		}

		out := dto.LabelResponse{
			AuditFields: l.AuditFields,
			Key:         l.Key,
			Value:       l.Value,
		}
		utils.WriteJSON(w, http.StatusCreated, out)
	}
}

// UpdateLabel godoc
// UpdateLabel godoc
//
//	@ID				UpdateLabel
//	@Summary		Update label (org scoped)
//	@Description	Partially update label fields.
//	@Tags			Labels
//	@Accept			json
//	@Produce		json
//	@Param			X-Org-ID	header		string					false	"Organization UUID"
//	@Param			id			path		string					true	"Label ID (UUID)"
//	@Param			body		body		dto.UpdateLabelRequest	true	"Fields to update"
//	@Success		200			{object}	dto.LabelResponse
//	@Failure		400			{string}	string	"invalid id / invalid json"
//	@Failure		401			{string}	string	"Unauthorized"
//	@Failure		403			{string}	string	"organization required"
//	@Failure		404			{string}	string	"not found"
//	@Failure		500			{string}	string	"update failed"
//	@Router			/labels/{id} [patch]
//	@Security		BearerAuth
//	@Security		OrgKeyAuth
//	@Security		OrgSecretAuth
func UpdateLabel(db *gorm.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
		if !ok {
			utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
			return
		}

		id, err := uuid.Parse(chi.URLParam(r, "id"))
		if err != nil {
			utils.WriteError(w, http.StatusBadRequest, "id_required", "id required")
			return
		}

		var l models.Label
		if err := db.Where("id = ? AND organization_id = ?", id, orgID).First(&l).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				utils.WriteError(w, http.StatusNotFound, "label_not_found", "label not found")
				return
			}
			utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
			return
		}

		var req dto.UpdateLabelRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			utils.WriteError(w, http.StatusBadRequest, "bad_request", "bad request")
			return
		}

		if req.Key != nil {
			l.Key = strings.TrimSpace(*req.Key)
		}
		if req.Value != nil {
			l.Value = strings.TrimSpace(*req.Value)
		}

		if err := db.Save(&l).Error; err != nil {
			utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
			return
		}

		out := dto.LabelResponse{
			AuditFields: l.AuditFields,
			Key:         l.Key,
			Value:       l.Value,
		}
		utils.WriteJSON(w, http.StatusOK, out)
	}
}

// DeleteLabel godoc
//
//	@ID				DeleteLabel
//	@Summary		Delete label (org scoped)
//	@Description	Permanently deletes the label.
//	@Tags			Labels
//	@Produce		json
//	@Param			X-Org-ID	header	string	false	"Organization UUID"
//	@Param			id			path	string	true	"Label ID (UUID)"
//	@Success		204			"No Content"
//	@Failure		400			{string}	string	"invalid id"
//	@Failure		401			{string}	string	"Unauthorized"
//	@Failure		403			{string}	string	"organization required"
//	@Failure		500			{string}	string	"delete failed"
//	@Router			/labels/{id} [delete]
//	@Security		BearerAuth
//	@Security		OrgKeyAuth
//	@Security		OrgSecretAuth
func DeleteLabel(db *gorm.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
		if !ok {
			utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
			return
		}

		id, err := uuid.Parse(chi.URLParam(r, "id"))
		if err != nil {
			utils.WriteError(w, http.StatusBadRequest, "id_required", "id required")
			return
		}

		if err := db.Where("id = ? AND organization_id = ?", id, orgID).Delete(&models.Label{}).Error; err != nil {
			utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
			return
		}
		w.WriteHeader(http.StatusNoContent)
	}
}
