// File Path: monorepo/cloud/maplepress-backend/pkg/search/search.go
package search

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	"go.uber.org/zap"
)

// SearchRequest represents a search request
type SearchRequest struct {
	Query  string
	Limit  int64
	Offset int64
	Filter string // e.g., "status = publish"
}

// SearchResult represents a search result
type SearchResult struct {
	Hits             []map[string]interface{} `json:"hits"`
	Query            string                   `json:"query"`
	ProcessingTimeMs int64                    `json:"processing_time_ms"`
	TotalHits        int64                    `json:"total_hits"`
	Limit            int64                    `json:"limit"`
	Offset           int64                    `json:"offset"`
}

// Search performs a search query on the index
func (c *Client) Search(siteID string, req SearchRequest) (*SearchResult, error) {
	indexName := c.GetIndexName(siteID)

	c.logger.Info("initiating search",
		zap.String("site_id", siteID),
		zap.String("index_name", indexName),
		zap.String("query", req.Query),
		zap.Int64("limit", req.Limit),
		zap.Int64("offset", req.Offset),
		zap.String("filter", req.Filter))

	// Build search request manually to avoid hybrid field
	searchBody := map[string]interface{}{
		"q":                     req.Query,
		"limit":                 req.Limit,
		"offset":                req.Offset,
		"attributesToHighlight": []string{"title", "excerpt", "content"},
	}

	// Add filter if provided
	if req.Filter != "" {
		searchBody["filter"] = req.Filter
	}

	// Marshal to JSON
	jsonData, err := json.Marshal(searchBody)
	if err != nil {
		c.logger.Error("failed to marshal search request", zap.Error(err))
		return nil, fmt.Errorf("failed to marshal search request: %w", err)
	}

	// Uncomment for debugging: shows exact JSON payload sent to Meilisearch
	// c.logger.Debug("search request payload", zap.String("json", string(jsonData)))

	// Build search URL
	searchURL := fmt.Sprintf("%s/indexes/%s/search", c.config.Host, indexName)
	// Uncomment for debugging: shows the Meilisearch API endpoint
	// c.logger.Debug("search URL", zap.String("url", searchURL))

	// Create HTTP request
	httpReq, err := http.NewRequest("POST", searchURL, bytes.NewBuffer(jsonData))
	if err != nil {
		c.logger.Error("failed to create HTTP request", zap.Error(err))
		return nil, fmt.Errorf("failed to create search request: %w", err)
	}

	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.config.APIKey))

	// Execute request
	httpClient := &http.Client{}
	resp, err := httpClient.Do(httpReq)
	if err != nil {
		c.logger.Error("failed to execute HTTP request", zap.Error(err))
		return nil, fmt.Errorf("failed to execute search request: %w", err)
	}
	if resp == nil {
		c.logger.Error("received nil response from search API")
		return nil, fmt.Errorf("received nil response from search API")
	}
	defer resp.Body.Close()

	c.logger.Info("received search response",
		zap.Int("status_code", resp.StatusCode),
		zap.String("status", resp.Status))

	// Read response body for logging
	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		c.logger.Error("failed to read response body", zap.Error(err))
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}

	// Uncomment for debugging: shows full Meilisearch response
	// c.logger.Debug("search response body", zap.String("body", string(bodyBytes)))

	if resp.StatusCode != http.StatusOK {
		c.logger.Error("search request failed",
			zap.Int("status_code", resp.StatusCode),
			zap.String("response_body", string(bodyBytes)))
		return nil, fmt.Errorf("search request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	// Parse response
	var searchResp struct {
		Hits               []map[string]interface{} `json:"hits"`
		Query              string                   `json:"query"`
		ProcessingTimeMs   int64                    `json:"processingTimeMs"`
		EstimatedTotalHits int                      `json:"estimatedTotalHits"`
		Limit              int64                    `json:"limit"`
		Offset             int64                    `json:"offset"`
	}

	if err := json.Unmarshal(bodyBytes, &searchResp); err != nil {
		c.logger.Error("failed to decode search response", zap.Error(err))
		return nil, fmt.Errorf("failed to decode search response: %w", err)
	}

	c.logger.Info("search completed successfully",
		zap.Int("hits_count", len(searchResp.Hits)),
		zap.Int("total_hits", searchResp.EstimatedTotalHits),
		zap.Int64("processing_time_ms", searchResp.ProcessingTimeMs))

	// Convert response
	result := &SearchResult{
		Hits:             searchResp.Hits,
		Query:            searchResp.Query,
		ProcessingTimeMs: searchResp.ProcessingTimeMs,
		TotalHits:        int64(searchResp.EstimatedTotalHits),
		Limit:            req.Limit,
		Offset:           req.Offset,
	}

	return result, nil
}

// SearchWithHighlights performs a search with custom highlighting
// Note: Currently uses same implementation as Search()
func (c *Client) SearchWithHighlights(siteID string, req SearchRequest, highlightTags []string) (*SearchResult, error) {
	// For now, just use the regular Search method
	// TODO: Implement custom highlight tags if needed
	return c.Search(siteID, req)
}
