package handlers

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

	"gauth-central/config"
	"gauth-central/internal/services"
	"gauth-central/pkg/utils"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type PostCategoryHandler struct {
	categoryService *services.PostCategoryService
}

func NewPostCategoryHandler(categoryService *services.PostCategoryService) *PostCategoryHandler {
	return &PostCategoryHandler{categoryService: categoryService}
}

// GetAllPostCategories godoc
// @Summary Get all active post categories
// @Description Retrieve a list of active post categories
// @Tags post-categories
// @Produce json
// @Success 200 {array} models.PostCategory
// @Failure 500 {object} map[string]string
// @Router /post-categories [get]
// GetAllPostCategories returns active post categories.
func (h *PostCategoryHandler) GetAllPostCategories(c *gin.Context) {
	items, err := h.categoryService.GetAllPostCategories(true)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, items)
}

// GetPostCategoryBySlug godoc
// @Summary Get post category by slug
// @Description Retrieve an active post category by slug
// @Tags post-categories
// @Produce json
// @Param slug path string true "Category Slug"
// @Success 200 {object} models.PostCategory
// @Failure 404 {object} map[string]string
// @Router /post-categories/{slug} [get]
// GetPostCategoryBySlug returns a post category by slug.
func (h *PostCategoryHandler) GetPostCategoryBySlug(c *gin.Context) {
	slug := c.Param("slug")
	item, err := h.categoryService.GetPostCategoryBySlug(slug, true)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, item)
}

// AdminGetAllPostCategories godoc
// @Summary Get all post categories (Admin)
// @Description Retrieve a list of all post categories including inactive ones
// @Tags admin
// @Security ApiKeyAuth
// @Produce json
// @Success 200 {array} models.PostCategory
// @Failure 500 {object} map[string]string
// @Router /admin/post-categories [get]
// AdminGetAllPostCategories returns all categories.
func (h *PostCategoryHandler) AdminGetAllPostCategories(c *gin.Context) {
	items, err := h.categoryService.GetAllPostCategories(false)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, items)
}

// AdminGetPostCategoryByID godoc
// @Summary Get post category by ID (Admin)
// @Description Retrieve a post category by ID
// @Tags admin
// @Security ApiKeyAuth
// @Produce json
// @Param id path string true "Category ID"
// @Success 200 {object} models.PostCategory
// @Failure 404 {object} map[string]string
// @Router /admin/post-categories/{id} [get]
// AdminGetPostCategoryByID returns a category by ID.
func (h *PostCategoryHandler) AdminGetPostCategoryByID(c *gin.Context) {
	id := c.Param("id")
	item, err := h.categoryService.GetPostCategoryByID(id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, item)
}

// CreatePostCategory godoc
// @Summary Create a post category (Admin)
// @Description Create a new post category
// @Tags admin
// @Security ApiKeyAuth
// @Accept multipart/form-data
// @Produce json
// @Param title formData string true "Title"
// @Param keywords formData string true "Keywords"
// @Param description formData string true "Description"
// @Param order formData int false "Order"
// @Param parent_id formData string false "Parent ID"
// @Param image formData file false "Image"
// @Param is_active formData bool false "Is active"
// @Success 201 {object} models.PostCategory
// @Failure 400 {object} map[string]string
// @Router /admin/post-categories [post]
// CreatePostCategory creates a new category.
func (h *PostCategoryHandler) CreatePostCategory(c *gin.Context) {
	title := strings.TrimSpace(c.PostForm("title"))
	keywords := strings.TrimSpace(c.PostForm("keywords"))
	description := strings.TrimSpace(c.PostForm("description"))
	if title == "" || keywords == "" || description == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "title, keywords, and description are required"})
		return
	}

	order := 1
	if rawOrder := strings.TrimSpace(c.PostForm("order")); rawOrder != "" {
		parsed, err := strconv.Atoi(rawOrder)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "order must be an integer"})
			return
		}
		order = parsed
	}

	parentID, err := parseUUIDPtr(c.PostForm("parent_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent_id"})
		return
	}

	isActive := true
	isActivePtr, err := parseOptionalBool(c, "is_active")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "is_active must be true or false"})
		return
	}
	if isActivePtr != nil {
		isActive = *isActivePtr
	}

	imageURL := ""
	file, fileErr := c.FormFile("image")
	if fileErr == nil {
		if file.Size > 5*1024*1024 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"})
			return
		}

		saved, saveErr := utils.SaveOptimizedImage(file, "./uploads/post-categories", "category", &utils.ImageOptions{
			Width:   config.AppConfig.PostCategoryImageWidth,
			Height:  config.AppConfig.PostCategoryImageHeight,
			Quality: float32(config.AppConfig.PostCategoryImageQuality),
			Format:  config.AppConfig.PostCategoryImageFormat,
			Mode:    config.AppConfig.PostCategoryImageMode,
		})
		if saveErr != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()})
			return
		}
		imageURL = saved
	}

	item, err := h.categoryService.CreatePostCategory(
		title,
		keywords,
		description,
		imageURL,
		order,
		parentID,
		isActive,
	)
	if err != nil {
		status := http.StatusInternalServerError
		if err.Error() == "slug cannot be empty" || err.Error() == "slug already exists" {
			status = http.StatusBadRequest
		}
		c.JSON(status, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, item)
}

// UpdatePostCategory godoc
// @Summary Update a post category (Admin)
// @Description Update an existing post category
// @Tags admin
// @Security ApiKeyAuth
// @Accept multipart/form-data
// @Produce json
// @Param id path string true "Category ID"
// @Param title formData string false "Title"
// @Param keywords formData string false "Keywords"
// @Param description formData string false "Description"
// @Param order formData int false "Order"
// @Param parent_id formData string false "Parent ID"
// @Param image formData file false "Image"
// @Param slug formData string false "Slug"
// @Param is_active formData bool false "Is active"
// @Success 200 {object} models.PostCategory
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /admin/post-categories/{id} [put]
// UpdatePostCategory updates a category.
func (h *PostCategoryHandler) UpdatePostCategory(c *gin.Context) {
	id := c.Param("id")

	title, hasTitle := getOptionalFormValue(c, "title")
	keywords, hasKeywords := getOptionalFormValue(c, "keywords")
	description, hasDescription := getOptionalFormValue(c, "description")
	slug, hasSlug := getOptionalFormValue(c, "slug")

	var titlePtr *string
	var keywordsPtr *string
	var descriptionPtr *string
	var slugPtr *string

	if hasTitle {
		titlePtr = &title
	}
	if hasKeywords {
		keywordsPtr = &keywords
	}
	if hasDescription {
		descriptionPtr = &description
	}
	if hasSlug {
		slugPtr = &slug
	}

	var orderPtr *int
	if rawOrder, hasOrder := getOptionalFormValue(c, "order"); hasOrder {
		if rawOrder == "" {
			orderPtr = nil
		} else {
			parsed, err := strconv.Atoi(rawOrder)
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": "order must be an integer"})
				return
			}
			orderPtr = &parsed
		}
	}

	parentIDValue, hasParent := getOptionalFormValue(c, "parent_id")
	parentIDSet := false
	var parentIDPtr *uuid.UUID
	if hasParent {
		parentIDSet = true
		if parentIDValue == "" {
			parentIDPtr = nil
		} else {
			parsed, err := parseUUIDPtr(parentIDValue)
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent_id"})
				return
			}
			parentIDPtr = parsed
		}
	}

	isActivePtr, err := parseOptionalBool(c, "is_active")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "is_active must be true or false"})
		return
	}

	var imagePtr *string
	file, fileErr := c.FormFile("image")
	if fileErr == nil {
		if file.Size > 5*1024*1024 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"})
			return
		}

		item, fetchErr := h.categoryService.GetPostCategoryByID(id)
		if fetchErr != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": fetchErr.Error()})
			return
		}

		if item.Image != "" && strings.HasPrefix(item.Image, "/uploads/") {
			_ = os.Remove("." + item.Image)
		}

		saved, saveErr := utils.SaveOptimizedImage(file, "./uploads/post-categories", id, &utils.ImageOptions{
			Width:   config.AppConfig.PostCategoryImageWidth,
			Height:  config.AppConfig.PostCategoryImageHeight,
			Quality: float32(config.AppConfig.PostCategoryImageQuality),
			Format:  config.AppConfig.PostCategoryImageFormat,
			Mode:    config.AppConfig.PostCategoryImageMode,
		})
		if saveErr != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()})
			return
		}
		imagePtr = &saved
	}

	item, err := h.categoryService.UpdatePostCategory(
		id,
		titlePtr,
		keywordsPtr,
		descriptionPtr,
		imagePtr,
		orderPtr,
		parentIDPtr,
		parentIDSet,
		slugPtr,
		isActivePtr,
	)
	if err != nil {
		status := http.StatusInternalServerError
		switch err.Error() {
		case "post category not found":
			status = http.StatusNotFound
		case "slug cannot be empty", "slug already exists":
			status = http.StatusBadRequest
		}
		c.JSON(status, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, item)
}

// DeletePostCategory godoc
// @Summary Delete a post category (Admin)
// @Description Delete a post category by ID
// @Tags admin
// @Security ApiKeyAuth
// @Param id path string true "Category ID"
// @Success 200 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /admin/post-categories/{id} [delete]
// DeletePostCategory deletes a category by ID.
func (h *PostCategoryHandler) DeletePostCategory(c *gin.Context) {
	id := c.Param("id")
	if err := h.categoryService.DeletePostCategory(id); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Post category deleted successfully"})
}
