package search

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

// SearXNG implements Provider using a self-hosted SearXNG instance.
// Suitable for airgapped deployments. Requires a configured endpoint.
type SearXNG struct {
	endpoint string // base URL, e.g. "https://search.internal.example.com"
	apiKey   string // optional API key (sent as X-API-Key header)
}

func (s *SearXNG) Name() string { return "searxng" }

// Configure sets the SearXNG endpoint and optional API key.
func (s *SearXNG) Configure(endpoint, apiKey string) {
	s.endpoint = endpoint
	s.apiKey = apiKey
}

// Endpoint returns the configured endpoint (for admin display).
func (s *SearXNG) Endpoint() string { return s.endpoint }

func (s *SearXNG) Search(ctx context.Context, query string, maxResults int) ([]SearchResult, error) {
	if s.endpoint == "" {
		return nil, fmt.Errorf("SearXNG endpoint not configured")
	}

	if maxResults <= 0 {
		maxResults = 5
	}
	if maxResults > 10 {
		maxResults = 10
	}

	// Build request URL: /search?q=...&format=json
	u, err := url.Parse(s.endpoint)
	if err != nil {
		return nil, fmt.Errorf("invalid SearXNG endpoint: %w", err)
	}
	u.Path = "/search"
	q := u.Query()
	q.Set("q", query)
	q.Set("format", "json")
	u.RawQuery = q.Encode()

	req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("build request: %w", err)
	}
	req.Header.Set("User-Agent", "ChatSwitchboard/1.0 (Web Search Tool)")
	if s.apiKey != "" {
		req.Header.Set("X-API-Key", s.apiKey)
	}

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("SearXNG request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("SearXNG returned HTTP %d", resp.StatusCode)
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, 512*1024))
	if err != nil {
		return nil, fmt.Errorf("read SearXNG response: %w", err)
	}

	return parseSearXNGJSON(body, maxResults)
}

// SearXNG JSON response format
type searxngResponse struct {
	Results []struct {
		Title   string `json:"title"`
		URL     string `json:"url"`
		Content string `json:"content"` // snippet
	} `json:"results"`
}

func parseSearXNGJSON(data []byte, maxResults int) ([]SearchResult, error) {
	var resp searxngResponse
	if err := json.Unmarshal(data, &resp); err != nil {
		return nil, fmt.Errorf("parse SearXNG response: %w", err)
	}

	results := make([]SearchResult, 0, maxResults)
	for _, r := range resp.Results {
		if len(results) >= maxResults {
			break
		}
		if r.URL == "" {
			continue
		}
		results = append(results, SearchResult{
			Title:   r.Title,
			URL:     r.URL,
			Snippet: r.Content,
		})
	}
	return results, nil
}

func init() {
	RegisterProvider(&SearXNG{})
}
