package selection_queries

import (
	"encoding/json"
	"fmt"
	"sort"
	"strings"
)

func nodeKey(termType, iri string) string {
	return termType + "\x00" + iri
}

func valuesTerm(n NodeRef) string {
	if n.TermType == "uri" {
		if n.IRI == "" {
			return ""
		}
		return "<" + n.IRI + ">"
	}
	if n.TermType == "bnode" {
		if n.IRI == "" {
			return ""
		}
		if strings.HasPrefix(n.IRI, "_:") {
			return n.IRI
		}
		return "_:" + n.IRI
	}
	return ""
}

func termKeyFromSparqlTerm(term sparqlTerm, includeBNodes bool) (string, bool) {
	if term.Type == "" || term.Value == "" {
		return "", false
	}
	if term.Type == "literal" {
		return "", false
	}
	if term.Type == "bnode" {
		if !includeBNodes {
			return "", false
		}
		return nodeKey("bnode", "_:"+term.Value), true
	}
	if term.Type == "uri" {
		return nodeKey("uri", term.Value), true
	}
	return "", false
}

func selectedNodesFromIDs(idx Index, selectedIDs []uint32, includeBNodes bool) ([]NodeRef, map[uint32]struct{}) {
	out := make([]NodeRef, 0, len(selectedIDs))
	set := make(map[uint32]struct{}, len(selectedIDs))
	for _, nid := range selectedIDs {
		n, ok := idx.IDToNode[nid]
		if !ok {
			continue
		}
		if n.TermType == "bnode" && !includeBNodes {
			continue
		}
		out = append(out, n)
		set[nid] = struct{}{}
	}
	return out, set
}

func idsFromBindings(raw []byte, varName string, idx Index, selectedSet map[uint32]struct{}, includeBNodes bool) ([]uint32, error) {
	var res sparqlResponse
	if err := json.Unmarshal(raw, &res); err != nil {
		return nil, fmt.Errorf("failed to parse SPARQL JSON: %w", err)
	}

	neighborSet := make(map[uint32]struct{})
	for _, b := range res.Results.Bindings {
		term, ok := b[varName]
		if !ok {
			continue
		}
		key, ok := termKeyFromSparqlTerm(term, includeBNodes)
		if !ok {
			continue
		}
		nid, ok := idx.KeyToID[key]
		if !ok {
			continue
		}
		if _, sel := selectedSet[nid]; sel {
			continue
		}
		neighborSet[nid] = struct{}{}
	}

	ids := make([]uint32, 0, len(neighborSet))
	for nid := range neighborSet {
		ids = append(ids, nid)
	}
	sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
	return ids, nil
}
