package handlers

import (
	"net/http"
	"strconv"
	"time"

	"github.com/labstack/echo/v4"
	"gorm.io/gorm"

	"github.com/treytartt/honeydue-api/internal/admin/dto"
	"github.com/treytartt/honeydue-api/internal/models"
)

// AdminPromotionHandler handles admin promotion management endpoints
type AdminPromotionHandler struct {
	db *gorm.DB
}

// NewAdminPromotionHandler creates a new admin promotion handler
func NewAdminPromotionHandler(db *gorm.DB) *AdminPromotionHandler {
	return &AdminPromotionHandler{db: db}
}

// PromotionResponse represents a promotion in API responses
type PromotionResponse struct {
	ID          uint    `json:"id"`
	PromotionID string  `json:"promotion_id"`
	Title       string  `json:"title"`
	Message     string  `json:"message"`
	Link        *string `json:"link"`
	StartDate   string  `json:"start_date"`
	EndDate     string  `json:"end_date"`
	TargetTier  string  `json:"target_tier"`
	IsActive    bool    `json:"is_active"`
	CreatedAt   string  `json:"created_at"`
	UpdatedAt   string  `json:"updated_at"`
}

// List handles GET /api/admin/promotions
func (h *AdminPromotionHandler) List(c echo.Context) error {
	var filters dto.PaginationParams
	if err := c.Bind(&filters); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	var promotions []models.Promotion
	var total int64

	query := h.db.Model(&models.Promotion{})

	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Where("promotion_id ILIKE ? OR title ILIKE ? OR message ILIKE ?", search, search, search)
	}

	query.Count(&total)

	// Apply sorting (allowlist prevents SQL injection via sort_by parameter)
	sortBy := filters.GetSafeSortBy([]string{
		"id", "promotion_id", "title", "start_date", "end_date",
		"target_tier", "is_active", "created_at", "updated_at",
	}, "created_at")
	query = query.Order(sortBy + " " + filters.GetSortDir())
	query = query.Offset(filters.GetOffset()).Limit(filters.GetPerPage())

	if err := query.Find(&promotions).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch promotions"})
	}

	responses := make([]PromotionResponse, len(promotions))
	for i, p := range promotions {
		responses[i] = PromotionResponse{
			ID:          p.ID,
			PromotionID: p.PromotionID,
			Title:       p.Title,
			Message:     p.Message,
			Link:        p.Link,
			StartDate:   p.StartDate.Format("2006-01-02T15:04:05Z"),
			EndDate:     p.EndDate.Format("2006-01-02T15:04:05Z"),
			TargetTier:  string(p.TargetTier),
			IsActive:    p.IsActive,
			CreatedAt:   p.CreatedAt.Format("2006-01-02T15:04:05Z"),
			UpdatedAt:   p.UpdatedAt.Format("2006-01-02T15:04:05Z"),
		}
	}

	return c.JSON(http.StatusOK, dto.NewPaginatedResponse(responses, total, filters.GetPage(), filters.GetPerPage()))
}

// Get handles GET /api/admin/promotions/:id
func (h *AdminPromotionHandler) Get(c echo.Context) error {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid ID"})
	}

	var promotion models.Promotion
	if err := h.db.First(&promotion, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Promotion not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch promotion"})
	}

	response := PromotionResponse{
		ID:          promotion.ID,
		PromotionID: promotion.PromotionID,
		Title:       promotion.Title,
		Message:     promotion.Message,
		Link:        promotion.Link,
		StartDate:   promotion.StartDate.Format("2006-01-02T15:04:05Z"),
		EndDate:     promotion.EndDate.Format("2006-01-02T15:04:05Z"),
		TargetTier:  string(promotion.TargetTier),
		IsActive:    promotion.IsActive,
		CreatedAt:   promotion.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:   promotion.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	}

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

// Create handles POST /api/admin/promotions
func (h *AdminPromotionHandler) Create(c echo.Context) error {
	var req struct {
		PromotionID string  `json:"promotion_id" validate:"required"`
		Title       string  `json:"title" validate:"required"`
		Message     string  `json:"message" validate:"required"`
		Link        *string `json:"link"`
		StartDate   string  `json:"start_date" validate:"required"`
		EndDate     string  `json:"end_date" validate:"required"`
		TargetTier  string  `json:"target_tier"`
		IsActive    *bool   `json:"is_active"`
	}

	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}
	if err := c.Validate(&req); err != nil {
		return err
	}

	startDate, err := time.Parse("2006-01-02T15:04:05Z", req.StartDate)
	if err != nil {
		startDate, err = time.Parse("2006-01-02", req.StartDate)
		if err != nil {
			return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid start_date format"})
		}
	}

	endDate, err := time.Parse("2006-01-02T15:04:05Z", req.EndDate)
	if err != nil {
		endDate, err = time.Parse("2006-01-02", req.EndDate)
		if err != nil {
			return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid end_date format"})
		}
	}

	targetTier := models.TierFree
	if req.TargetTier == "pro" {
		targetTier = models.TierPro
	}

	promotion := models.Promotion{
		PromotionID: req.PromotionID,
		Title:       req.Title,
		Message:     req.Message,
		Link:        req.Link,
		StartDate:   startDate,
		EndDate:     endDate,
		TargetTier:  targetTier,
		IsActive:    true,
	}

	if req.IsActive != nil {
		promotion.IsActive = *req.IsActive
	}

	if err := h.db.Create(&promotion).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to create promotion"})
	}

	return c.JSON(http.StatusCreated, PromotionResponse{
		ID:          promotion.ID,
		PromotionID: promotion.PromotionID,
		Title:       promotion.Title,
		Message:     promotion.Message,
		Link:        promotion.Link,
		StartDate:   promotion.StartDate.Format("2006-01-02T15:04:05Z"),
		EndDate:     promotion.EndDate.Format("2006-01-02T15:04:05Z"),
		TargetTier:  string(promotion.TargetTier),
		IsActive:    promotion.IsActive,
		CreatedAt:   promotion.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:   promotion.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	})
}

// Update handles PUT /api/admin/promotions/:id
func (h *AdminPromotionHandler) Update(c echo.Context) error {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid ID"})
	}

	var promotion models.Promotion
	if err := h.db.First(&promotion, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Promotion not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch promotion"})
	}

	var req struct {
		PromotionID *string `json:"promotion_id"`
		Title       *string `json:"title"`
		Message     *string `json:"message"`
		Link        *string `json:"link"`
		StartDate   *string `json:"start_date"`
		EndDate     *string `json:"end_date"`
		TargetTier  *string `json:"target_tier"`
		IsActive    *bool   `json:"is_active"`
	}

	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	if req.PromotionID != nil {
		promotion.PromotionID = *req.PromotionID
	}
	if req.Title != nil {
		promotion.Title = *req.Title
	}
	if req.Message != nil {
		promotion.Message = *req.Message
	}
	if req.Link != nil {
		promotion.Link = req.Link
	}
	if req.StartDate != nil {
		startDate, err := time.Parse("2006-01-02T15:04:05Z", *req.StartDate)
		if err != nil {
			startDate, err = time.Parse("2006-01-02", *req.StartDate)
			if err != nil {
				return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid start_date format"})
			}
		}
		promotion.StartDate = startDate
	}
	if req.EndDate != nil {
		endDate, err := time.Parse("2006-01-02T15:04:05Z", *req.EndDate)
		if err != nil {
			endDate, err = time.Parse("2006-01-02", *req.EndDate)
			if err != nil {
				return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid end_date format"})
			}
		}
		promotion.EndDate = endDate
	}
	if req.TargetTier != nil {
		if *req.TargetTier == "pro" {
			promotion.TargetTier = models.TierPro
		} else {
			promotion.TargetTier = models.TierFree
		}
	}
	if req.IsActive != nil {
		promotion.IsActive = *req.IsActive
	}

	if err := h.db.Save(&promotion).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to update promotion"})
	}

	return c.JSON(http.StatusOK, PromotionResponse{
		ID:          promotion.ID,
		PromotionID: promotion.PromotionID,
		Title:       promotion.Title,
		Message:     promotion.Message,
		Link:        promotion.Link,
		StartDate:   promotion.StartDate.Format("2006-01-02T15:04:05Z"),
		EndDate:     promotion.EndDate.Format("2006-01-02T15:04:05Z"),
		TargetTier:  string(promotion.TargetTier),
		IsActive:    promotion.IsActive,
		CreatedAt:   promotion.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:   promotion.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	})
}

// Delete handles DELETE /api/admin/promotions/:id
func (h *AdminPromotionHandler) Delete(c echo.Context) error {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid ID"})
	}

	result := h.db.Delete(&models.Promotion{}, id)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete promotion"})
	}

	if result.RowsAffected == 0 {
		return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Promotion not found"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"message": "Promotion deleted successfully"})
}
