package plugin

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

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config/constants"
	pagedto "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/dto/page"
	pageservice "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/page"
	pageusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/page"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httperror"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpresponse"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpvalidation"
)

// SearchPagesHandler handles page search from WordPress plugin
type SearchPagesHandler struct {
	searchService pageservice.SearchPagesService
	logger        *zap.Logger
}

// ProvideSearchPagesHandler creates a new SearchPagesHandler
func ProvideSearchPagesHandler(
	searchService pageservice.SearchPagesService,
	logger *zap.Logger,
) *SearchPagesHandler {
	return &SearchPagesHandler{
		searchService: searchService,
		logger:        logger,
	}
}

// Handle handles the HTTP request for searching pages
// This endpoint is protected by API key middleware
func (h *SearchPagesHandler) Handle(w http.ResponseWriter, r *http.Request) {
	// Get site information from context (populated by API key middleware)
	isAuthenticated, ok := r.Context().Value(constants.SiteIsAuthenticated).(bool)
	if !ok || !isAuthenticated {
		h.logger.Error("site not authenticated in context")
		httperror.ProblemUnauthorized(w, "Invalid API key")
		return
	}

	// Extract site ID and tenant ID from context
	siteIDStr, _ := r.Context().Value(constants.SiteID).(string)
	tenantIDStr, _ := r.Context().Value(constants.SiteTenantID).(string)

	h.logger.Info("search pages request",
		zap.String("tenant_id", tenantIDStr),
		zap.String("site_id", siteIDStr))

	// Parse IDs
	tenantID, err := gocql.ParseUUID(tenantIDStr)
	if err != nil {
		h.logger.Error("invalid tenant ID format", zap.Error(err))
		httperror.ProblemBadRequest(w, "invalid tenant ID")
		return
	}

	siteID, err := gocql.ParseUUID(siteIDStr)
	if err != nil {
		h.logger.Error("invalid site ID format", zap.Error(err))
		httperror.ProblemBadRequest(w, "invalid site ID")
		return
	}

	// CWE-436: Validate Content-Type before parsing
	if err := httpvalidation.ValidateJSONContentType(r); err != nil {
		httperror.ProblemBadRequest(w, err.Error())
		return
	}

	// Parse request body
	var req pagedto.SearchRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httperror.ProblemBadRequest(w, "invalid request body")
		return
	}

	// Validate request
	if req.Query == "" {
		httperror.ProblemBadRequest(w, "query is required")
		return
	}

	// Convert DTO to use case input
	input := &pageusecase.SearchPagesInput{
		Query:  req.Query,
		Limit:  req.Limit,
		Offset: req.Offset,
		Filter: req.Filter,
	}

	// Call service
	output, err := h.searchService.SearchPages(r.Context(), tenantID, siteID, input)
	if err != nil {
		h.logger.Error("failed to search pages",
			zap.Error(err),
			zap.String("site_id", siteIDStr),
			zap.String("query", req.Query))

		// Check for specific errors
		if err.Error() == "site not found" {
			httperror.ProblemNotFound(w, "site not found")
			return
		}
		if err.Error() == "site is not verified" {
			httperror.ProblemForbidden(w, "site is not verified")
			return
		}

		httperror.ProblemInternalServerError(w, "failed to search pages")
		return
	}

	// Map to response DTO
	response := pagedto.SearchResponse{
		Hits:             output.Hits.([]map[string]interface{}),
		Query:            output.Query,
		ProcessingTimeMs: output.ProcessingTimeMs,
		TotalHits:        output.TotalHits,
		Limit:            output.Limit,
		Offset:           output.Offset,
	}

	h.logger.Info("pages searched successfully",
		zap.String("site_id", siteIDStr),
		zap.String("query", req.Query),
		zap.Int64("total_hits", output.TotalHits))

	httpresponse.OK(w, response)
}
