package dorks

import "fmt"

// Registry is the in-memory store of loaded dork definitions. It is built
// once at startup (via NewRegistry) and is safe for concurrent reads.
type Registry struct {
	dorks      []Dork
	byID       map[string]int
	bySource   map[string][]int
	byCategory map[string][]int
}

// NewRegistry loads every embedded dork YAML file, validates them, and
// returns a ready-to-query Registry. An empty definitions tree is tolerated
// and yields an empty (but non-nil) Registry.
func NewRegistry() (*Registry, error) {
	ds, err := loadDorks()
	if err != nil {
		return nil, fmt.Errorf("loading dorks: %w", err)
	}
	return NewRegistryFromDorks(ds), nil
}

// NewRegistryFromDorks builds a Registry from an explicit slice of dorks
// without touching the embedded filesystem. Intended for tests.
func NewRegistryFromDorks(ds []Dork) *Registry {
	r := &Registry{
		dorks:      make([]Dork, len(ds)),
		byID:       make(map[string]int, len(ds)),
		bySource:   make(map[string][]int),
		byCategory: make(map[string][]int),
	}
	copy(r.dorks, ds)
	for i, d := range r.dorks {
		r.byID[d.ID] = i
		r.bySource[d.Source] = append(r.bySource[d.Source], i)
		r.byCategory[d.Category] = append(r.byCategory[d.Category], i)
	}
	return r
}

// List returns all loaded dorks. The returned slice must not be mutated.
func (r *Registry) List() []Dork {
	return r.dorks
}

// Get returns the dork with the given id and a boolean indicating whether it
// was found.
func (r *Registry) Get(id string) (Dork, bool) {
	idx, ok := r.byID[id]
	if !ok {
		return Dork{}, false
	}
	return r.dorks[idx], true
}

// ListBySource returns every dork declared for the given source.
func (r *Registry) ListBySource(source string) []Dork {
	idxs := r.bySource[source]
	out := make([]Dork, 0, len(idxs))
	for _, i := range idxs {
		out = append(out, r.dorks[i])
	}
	return out
}

// ListByCategory returns every dork tagged with the given category.
func (r *Registry) ListByCategory(category string) []Dork {
	idxs := r.byCategory[category]
	out := make([]Dork, 0, len(idxs))
	for _, i := range idxs {
		out = append(out, r.dorks[i])
	}
	return out
}

// Stats returns aggregate counts grouped by source and category.
func (r *Registry) Stats() Stats {
	s := Stats{
		Total:      len(r.dorks),
		BySource:   make(map[string]int, len(r.bySource)),
		ByCategory: make(map[string]int, len(r.byCategory)),
	}
	for src, idxs := range r.bySource {
		s.BySource[src] = len(idxs)
	}
	for cat, idxs := range r.byCategory {
		s.ByCategory[cat] = len(idxs)
	}
	return s
}
