package verify

import "context"

// An Oracle is the thing that makes this a swarm of operators and not a swarm
// of guessers.
//
// The [Verifier] above re-asks a *stronger model* whether a finding is real.
// That is still a model grading a model — useful for triage, but it is not
// proof, and a confident model is wrong in exactly the cases that cost the most
// downstream. An Oracle is different in kind: it derives a hard verdict from a
// signal that lives OUTSIDE any model —
//
//   - an out-of-band callback actually fired on infrastructure we control,
//   - the attack response diverged from a known-good baseline response,
//   - a time-based payload crossed a statistical delay threshold.
//
// That external signal is the exact line between a scanner that reports "likely
// vulnerable" and an operator that reports "vulnerable, here is the receipt."
// When an oracle speaks decisively, the corroborator trusts it over any model
// claim — including the paid [Verifier].
//
// Oracles probe actively (network I/O, a listen window), so they are consulted
// only for the high-stakes, uncorroborated slice where proof is worth the cost,
// under a per-campaign cap, and they must honor scope / safe-mode in their own
// implementation. An oracle that cannot speak to a candidate returns
// [Inconclusive] and the corroborator falls through to the next oracle, then to
// the model verifier, then to the passive gate — proof is preferred, never
// required.
type Oracle interface {
	// Name identifies the oracle in proof strings and reporting (e.g.
	// "oob-callback", "differential-response", "timing").
	Name() string

	// Applies reports whether this oracle can meaningfully test the candidate:
	// the right vulnerability class, and any inputs it needs are present. It
	// must be cheap and side-effect-free — it runs before we decide to spend a
	// probe against the cap.
	Applies(c Candidate) bool

	// Probe actively tests the candidate and returns a hard, external verdict.
	// confidence is in [0,1]; proof is a short, human-readable receipt for the
	// report ("DNS interaction on a1b2c3.oast.fun from 203.0.113.9"). An error
	// means the probe could not be carried out (tool missing, network failure,
	// out of scope) — that is treated as [Inconclusive], never as a verdict,
	// so an infrastructure failure can neither confirm nor condemn a finding.
	Probe(ctx context.Context, c Candidate) (v OracleVerdict, confidence float64, proof string, err error)
}

// OracleVerdict is an oracle's external, non-model judgment about a candidate.
type OracleVerdict int

const (
	// Inconclusive: this oracle cannot speak to this candidate — wrong class,
	// a missing input, or no signal arrived in the window. The corroborator
	// falls through. The zero value, so a failed/empty probe is inconclusive
	// by default rather than accidentally decisive.
	Inconclusive OracleVerdict = iota
	// Proven: an external signal confirmed real impact. Final and trusted over
	// any model claim — promotes the candidate to a corroborated floor.
	Proven
	// Disproven: the external signal actively refuted the candidate (e.g. the
	// attack and baseline responses were identical). Final — rejects it.
	Disproven
)

// String renders the verdict for logs and proof strings.
func (v OracleVerdict) String() string {
	switch v {
	case Proven:
		return "proven"
	case Disproven:
		return "disproven"
	default:
		return "inconclusive"
	}
}

// Decisive reports whether the verdict is a hard yes/no the corroborator should
// act on (Proven or Disproven), as opposed to Inconclusive.
func (v OracleVerdict) Decisive() bool { return v == Proven || v == Disproven }

// provenFloor is the pheromone floor an oracle-proven finding is promoted to.
// It is higher than any model-verification floor on purpose: external ground
// truth outranks a model's own confidence. A proof lands at least here.
const provenFloor = 0.95
