package oracle

import (
	"context"
	"testing"
	"time"

	"github.com/Armur-Ai/Pentest-Swarm-AI/internal/swarm/verify"
)

// slowOracle blocks until ctx is done, then reports whether it was cancelled.
type slowOracle struct{ cancelled bool }

func (s *slowOracle) Name() string            { return "slow" }
func (s *slowOracle) Applies(verify.Candidate) bool { return true }
func (s *slowOracle) Probe(ctx context.Context, _ verify.Candidate) (verify.OracleVerdict, float64, string, error) {
	<-ctx.Done()
	s.cancelled = true
	return verify.Inconclusive, 0, "", ctx.Err()
}

func TestWithTimeout_BoundsProbe(t *testing.T) {
	s := &slowOracle{}
	o := WithTimeout(s, 20*time.Millisecond)

	start := time.Now()
	v, _, _, err := o.Probe(context.Background(), verify.Candidate{})
	elapsed := time.Since(start)

	if elapsed > 500*time.Millisecond {
		t.Errorf("probe took %v, timeout should have fired at ~20ms", elapsed)
	}
	if !s.cancelled || err == nil || v != verify.Inconclusive {
		t.Errorf("expected a cancelled inconclusive probe, got v=%v err=%v", v, err)
	}
}

func TestWithTimeout_ZeroIsNoop(t *testing.T) {
	s := &slowOracle{}
	if WithTimeout(s, 0) != verify.Oracle(s) {
		t.Error("zero timeout should return the oracle unchanged")
	}
}

func TestWithTimeout_PreservesNameAndApplies(t *testing.T) {
	o := WithTimeout(&fakeOracleT{}, time.Second)
	if o.Name() != "named" || !o.Applies(verify.Candidate{}) {
		t.Error("decorator should delegate Name/Applies")
	}
}

type fakeOracleT struct{}

func (fakeOracleT) Name() string                   { return "named" }
func (fakeOracleT) Applies(verify.Candidate) bool  { return true }
func (fakeOracleT) Probe(context.Context, verify.Candidate) (verify.OracleVerdict, float64, string, error) {
	return verify.Proven, 1, "x", nil
}
