package handlers

import (
	"bookhoard/internal/database"
	"bookhoard/internal/services"
	"encoding/json"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/labstack/echo/v5"
)

type FiltersHandler struct {
	db             *database.Queries
	filtersService *services.FiltersService
}

func NewFiltersHandler(db *database.Queries) *FiltersHandler {
	return &FiltersHandler{
		db:             db,
		filtersService: services.NewFiltersService(db),
	}
}

// CreateSavedFilterRequest - Request body for creating/updating filters
type CreateSavedFilterRequest struct {
	Name         string            `json:"name" validate:"required,min=1,max=100"`
	ResourceType string            `json:"resource_type" validate:"required,oneof=media-items collections devices"`
	Filters      map[string]string `json:"filters" validate:"required"`
}

// SavedFilterResponse - Response format (matches database model structure)
type SavedFilterResponse struct {
	ID           uuid.UUID       `json:"id"`
	Name         string          `json:"name"`
	ResourceType string          `json:"resource_type"`
	Filters      json.RawMessage `json:"filters"` // JSONB as raw bytes
	CreatedAt    string          `json:"created_at"`
	UpdatedAt    string          `json:"updated_at"`
}

// Helper function to check if request wants HTML response
func wantsHTML(header http.Header) bool {
	accept := header.Get("Accept")
	return accept != "" && (accept == "text/html" || strings.Contains(accept, "text/html"))
}

// GetSavedFilters - GET /api/saved-filters?resource_type=media-items
// Supports both JSON (API) and HTML (HTMX) responses
func (h *FiltersHandler) GetSavedFilters(c *echo.Context) error {
	user := c.Get("user").(database.Users)
	userUUID := uuid.UUID(user.ID.Bytes)

	resourceType := c.QueryParam("resource_type")
	if resourceType == "" {
		if wantsHTML(c.Request().Header) {
			return c.String(http.StatusBadRequest, "resource_type query parameter is required")
		}
		return c.JSON(http.StatusBadRequest, map[string]string{
			"error": "resource_type query parameter is required",
		})
	}

	filters, err := h.filtersService.GetSavedFilters(c.Request().Context(), userUUID, resourceType)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to fetch filters"})
	}

	// Return JSON for API requests
	response := make([]SavedFilterResponse, len(filters))
	for i, f := range filters {
		response[i] = SavedFilterResponse{
			ID:           uuid.Must(uuid.FromBytes(f.ID.Bytes[:])),
			Name:         f.Name,
			ResourceType: f.ResourceType,
			Filters:      f.Filters,
			CreatedAt:    f.CreatedAt.Time.Format(time.RFC3339),
			UpdatedAt:    f.UpdatedAt.Time.Format(time.RFC3339),
		}
	}

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

// GetSavedFilterByID - GET /api/saved-filters/:id
// Retrieve a single saved filter by ID
func (h *FiltersHandler) GetSavedFilterByID(c *echo.Context) error {
	user := c.Get("user").(database.Users)
	userUUID := uuid.UUID(user.ID.Bytes)

	// Parse filter ID from URL parameter
	filterID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{
			"error": "invalid filter ID",
		})
	}

	// Use service layer (includes business logic + ownership verification)
	filter, err := h.filtersService.GetSavedFilterByID(c.Request().Context(), userUUID, filterID)
	if err != nil {
		if errors.Is(err, services.ErrFilterNotFound) {
			return c.JSON(http.StatusNotFound, map[string]string{
				"error": "filter not found",
			})
		}
		return c.JSON(http.StatusInternalServerError, map[string]string{
			"error": "failed to fetch filter",
		})
	}

	// Return JSON response (JSONB handled automatically)
	response := SavedFilterResponse{
		ID:           filterID,
		Name:         filter.Name,
		ResourceType: filter.ResourceType,
		Filters:      json.RawMessage(filter.Filters), // Return JSONB as-is
		CreatedAt:    filter.CreatedAt.Time.Format(time.RFC3339),
		UpdatedAt:    filter.UpdatedAt.Time.Format(time.RFC3339),
	}

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

// CreateSavedFilter - POST /api/saved-filters
// Supports both JSON (API) and HTML (HTMX) responses
func (h *FiltersHandler) CreateSavedFilter(c *echo.Context) error {
	user := c.Get("user").(database.Users)
	userUUID := uuid.UUID(user.ID.Bytes)

	var req CreateSavedFilterRequest
	if err := c.Bind(&req); err != nil {
		if wantsHTML(c.Request().Header) {
			return c.String(http.StatusBadRequest, "Invalid request body")
		}
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})
	}

	if req.Name == "" || req.ResourceType == "" {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "name and resource_type are required"})
	}

	filter, err := h.filtersService.CreateSavedFilter(c.Request().Context(), userUUID, req.Name, req.ResourceType, req.Filters)
	if err != nil {
		if err.Error() == "filter with name '"+req.Name+"' already exists for this resource type" {
			if wantsHTML(c.Request().Header) {
				return c.String(http.StatusConflict, "Filter with this name already exists")
			}
			return c.JSON(http.StatusConflict, map[string]string{"error": err.Error()})
		}
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to create filter"})
	}

	// Return JSON response (JSONB handled automatically)
	response := SavedFilterResponse{
		ID:           uuid.Must(uuid.FromBytes(filter.ID.Bytes[:])),
		Name:         filter.Name,
		ResourceType: filter.ResourceType,
		Filters:      json.RawMessage(filter.Filters),
		CreatedAt:    filter.CreatedAt.Time.Format(time.RFC3339),
		UpdatedAt:    filter.UpdatedAt.Time.Format(time.RFC3339),
	}

	// Add HX-Redirect for HTMX requests
	if c.Request().Header.Get("HX-Request") == "true" {
		c.Response().Header().Set("HX-Redirect", "/bookshelf")
	}

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

// DeleteSavedFilter - DELETE /api/saved-filters/:id
// Supports both JSON (API) and HTML (HTMX) responses
func (h *FiltersHandler) DeleteSavedFilter(c *echo.Context) error {
	user := c.Get("user").(database.Users)
	userUUID := uuid.UUID(user.ID.Bytes)

	filterID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid filter ID"})
	}

	err = h.filtersService.DeleteSavedFilter(c.Request().Context(), userUUID, filterID)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return c.JSON(http.StatusNotFound, map[string]string{"error": "filter not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to delete filter"})
	}

	return c.NoContent(http.StatusNoContent)
}

// UpdateSavedFilter - PUT /api/saved-filters/:id
// Supports both JSON (API) and HTML (HTMX) responses
func (h *FiltersHandler) UpdateSavedFilter(c *echo.Context) error {
	user := c.Get("user").(database.Users)
	userUUID := uuid.UUID(user.ID.Bytes)

	filterID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid filter ID"})
	}

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

	filter, err := h.filtersService.UpdateSavedFilter(c.Request().Context(), userUUID, filterID, req.Name, req.Filters)
	if err != nil {
		if err.Error() == "filter with name '"+req.Name+"' already exists for this resource type" {
			return c.JSON(http.StatusConflict, map[string]string{"error": err.Error()})
		}
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to update filter"})
	}

	response := SavedFilterResponse{
		ID:           filterID,
		Name:         filter.Name,
		ResourceType: filter.ResourceType,
		Filters:      json.RawMessage(filter.Filters),
		CreatedAt:    filter.CreatedAt.Time.Format(time.RFC3339),
		UpdatedAt:    filter.UpdatedAt.Time.Format(time.RFC3339),
	}

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