package ucca

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
)

// FetchByNormIDs loads one representative unit per norm_id from the KB slice
// collection — the fetch side of the Concept->Norm recall injector. Returns
// LegalSearchResult with the caller-provided concept-relevance score (there is no
// similarity query; the injector places them by that score). Returns nil on any
// error or when no KB slice is configured (graceful degradation).
func (c *LegalRAGClient) FetchByNormIDs(ctx context.Context, normIDs []string, score float64) []LegalSearchResult {
	if c.kbSliceCollection == "" || len(normIDs) == 0 {
		return nil
	}
	should := make([]map[string]interface{}, 0, len(normIDs))
	for _, nid := range normIDs {
		should = append(should, map[string]interface{}{"key": "norm_id", "match": map[string]interface{}{"value": nid}})
	}
	reqBody := map[string]interface{}{
		"limit":        len(normIDs) * 3,
		"with_payload": true,
		"with_vectors": false,
		"filter":       map[string]interface{}{"should": should},
	}
	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return nil
	}
	url := fmt.Sprintf("%s/collections/%s/points/scroll", c.qdrantURL, c.kbSliceCollection)
	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonBody))
	if err != nil {
		return nil
	}
	req.Header.Set("Content-Type", "application/json")
	if c.qdrantAPIKey != "" {
		req.Header.Set("api-key", c.qdrantAPIKey)
	}
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil
	}
	defer func() { _ = resp.Body.Close() }()
	if resp.StatusCode != http.StatusOK {
		return nil
	}
	var scrollResp qdrantScrollResponse
	if err := json.NewDecoder(resp.Body).Decode(&scrollResp); err != nil {
		return nil
	}
	seen := map[string]bool{}
	out := make([]LegalSearchResult, 0, len(normIDs))
	for _, pt := range scrollResp.Result.Points {
		nid := getString(pt.Payload, "norm_id")
		if nid == "" || seen[nid] {
			continue
		}
		seen[nid] = true
		out = append(out, scrollPointToResult(pt.Payload, score))
	}
	return out
}

// scrollPointToResult maps a scroll-point payload to a LegalSearchResult. Mirrors
// hitsToResults' payload keys; the score is assigned by the caller (concept rank).
func scrollPointToResult(payload map[string]interface{}, score float64) LegalSearchResult {
	regCode := getString(payload, "regulation_code")
	if regCode == "" {
		regCode = getString(payload, "regulation_id")
	}
	return LegalSearchResult{
		Text:            getString(payload, "chunk_text"),
		RegulationCode:  regCode,
		RegulationName:  getString(payload, "regulation_name_de"),
		RegulationShort: getString(payload, "regulation_short"),
		Category:        getString(payload, "category"),
		Article:         getString(payload, "article"),
		ArticleLabel:    getString(payload, "article_label"),
		Paragraph:       getString(payload, "paragraph"),
		SourceURL:       getString(payload, "source_url"),
		CitationUnit:    getString(payload, "citation_unit"),
		Score:           score,
	}
}
