package executor

import (
	"fmt"

	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/parser"
)

// BindMarkerNames returns bind marker names ordered by their positional index.
// It returns an error if any bind marker is positional or names are duplicated.
func BindMarkerNames(stmt parser.Statement) ([]string, error) {
	markerIndex := buildBindMarkerIndex(stmt)
	if len(markerIndex) == 0 {
		return nil, nil
	}

	names := make([]string, len(markerIndex))
	seen := make(map[string]struct{}, len(markerIndex))
	for expr, idx := range markerIndex {
		named, ok := expr.(*parser.NamedBindMarker)
		if !ok {
			return nil, fmt.Errorf("named values require named bind markers")
		}
		if _, exists := seen[named.Name]; exists {
			return nil, fmt.Errorf("duplicate bind marker name %q", named.Name)
		}
		seen[named.Name] = struct{}{}
		if idx < 0 || idx >= len(names) {
			return nil, fmt.Errorf("bind marker index %d out of range", idx)
		}
		names[idx] = named.Name
	}
	for i, name := range names {
		if name == "" {
			return nil, fmt.Errorf("missing bind marker name at position %d", i)
		}
	}
	return names, nil
}

// PreparedBindMarkerNames returns bind marker names ordered by positional index.
// Unlike BindMarkerNames, duplicate names are preserved because prepared metadata
// describes marker occurrences, not the client-side mapping semantics.
func PreparedBindMarkerNames(stmt parser.Statement) ([]string, bool) {
	markerIndex := buildBindMarkerIndex(stmt)
	if len(markerIndex) == 0 {
		return nil, true
	}

	names := make([]string, len(markerIndex))
	for expr, idx := range markerIndex {
		named, ok := expr.(*parser.NamedBindMarker)
		if !ok {
			return nil, false
		}
		if idx < 0 || idx >= len(names) {
			return nil, false
		}
		names[idx] = named.Name
	}
	for _, name := range names {
		if name == "" {
			return nil, false
		}
	}
	return names, true
}

// BindMarkerCount returns the number of bind markers in the statement.
func BindMarkerCount(stmt parser.Statement) int {
	return countBindMarkersInStatement(stmt)
}

// BuildBindMarkerIndex exposes bind marker indexing for external callers.
func BuildBindMarkerIndex(stmt parser.Statement) map[parser.Expression]int {
	return buildBindMarkerIndex(stmt)
}
