// Package dorks provides YAML-embedded dork definitions and per-source
// executors for the KeyHunter dork engine. The package mirrors the
// pkg/providers pattern: dorks are authored as YAML files under
// pkg/dorks/definitions/{source}/*.yaml and compiled into the binary via
// go:embed, then loaded into an in-memory Registry at startup.
package dorks

import (
	"fmt"
	"strings"
)

// Dork is a single dork definition loaded from a YAML file under
// pkg/dorks/definitions/{source}/*.yaml.
type Dork struct {
	ID          string   `yaml:"id"`
	Name        string   `yaml:"name"`
	Source      string   `yaml:"source"`   // github|google|shodan|censys|zoomeye|fofa|gitlab|bing
	Category    string   `yaml:"category"` // frontier|specialized|infrastructure|emerging|enterprise
	Query       string   `yaml:"query"`
	Description string   `yaml:"description"`
	Tags        []string `yaml:"tags"`
}

// ValidSources enumerates the dork source backends recognised by the engine.
// Executors for most of these arrive in later phases (OSINT 9-16); the
// foundation plan only wires the GitHub executor live.
var ValidSources = []string{
	"github",
	"google",
	"shodan",
	"censys",
	"zoomeye",
	"fofa",
	"gitlab",
	"bing",
}

// ValidCategories enumerates the dork taxonomy buckets used for filtering.
var ValidCategories = []string{
	"frontier",
	"specialized",
	"infrastructure",
	"emerging",
	"enterprise",
}

// Validate returns a non-nil error when the dork is missing required fields
// or declares an unknown source.
func (d Dork) Validate() error {
	if strings.TrimSpace(d.ID) == "" {
		return fmt.Errorf("dork: id is required")
	}
	if strings.TrimSpace(d.Source) == "" {
		return fmt.Errorf("dork %q: source is required", d.ID)
	}
	if strings.TrimSpace(d.Query) == "" {
		return fmt.Errorf("dork %q: query is required", d.ID)
	}
	if !contains(ValidSources, d.Source) {
		return fmt.Errorf("dork %q: source %q is not one of %v", d.ID, d.Source, ValidSources)
	}
	return nil
}

// Stats holds aggregate counts over a Registry, returned by Registry.Stats.
type Stats struct {
	Total      int
	BySource   map[string]int
	ByCategory map[string]int
}

func contains(haystack []string, needle string) bool {
	for _, h := range haystack {
		if h == needle {
			return true
		}
	}
	return false
}
