package streamgate

import (
	"context"
	"errors"
)

const (
	// DefaultNoopFilterID is the default stable identifier for NoopFilter.
	DefaultNoopFilterID = "noop"

	// DefaultNoopChannel is the default channel name used by NoopFilter.
	DefaultNoopChannel = "default"

	// DefaultNoopConsumerID is the default filter consumer token for NoopFilter.
	DefaultNoopConsumerID = "streamgate.noop"

	// DefaultNoopRuleID is the default rule token for NoopFilter.
	DefaultNoopRuleID = "noop.pass"

	// DefaultNoopDescriptorCode is the default descriptor token for NoopFilter evidence.
	DefaultNoopDescriptorCode = "noop_pass"
)

// NoopFilter is a stable production Filter implementation backed by FilterBase.
// It always applies, imposes no hold requirements, and evaluates to a pass
// decision. Enablement and failure policy are owned by FilterRegistrySnapshot.
type NoopFilter struct {
	FilterBase
	channel string
}

// NewNoopFilter constructs a NoopFilter using the default filter ID "noop"
// and default channel "default".
func NewNoopFilter() (NoopFilter, error) {
	return NewNoopFilterWithChannel(DefaultNoopFilterID, DefaultNoopChannel)
}

// NewNoopFilterWithChannel constructs a NoopFilter with a specified filter ID
// and channel.
func NewNoopFilterWithChannel(id, channel string) (NoopFilter, error) {
	if id == "" {
		id = DefaultNoopFilterID
	}
	if channel == "" {
		channel = DefaultNoopChannel
	}
	base, err := NewFilterBase(id)
	if err != nil {
		return NoopFilter{}, err
	}
	return NoopFilter{
		FilterBase: base,
		channel:    channel,
	}, nil
}

// ID returns the stable filter identifier.
func (f NoopFilter) ID() string {
	return f.FilterBase.ID()
}

// Applies returns true for any filter context. Enablement and selection are
// managed externally by the FilterRegistry.
func (f NoopFilter) Applies(ctx FilterContext) bool {
	return true
}

// HoldRequirement returns a FilterHoldRequirement of mode none, indicating
// that NoopFilter does not block or hold events.
func (f NoopFilter) HoldRequirement(ctx FilterContext) FilterHoldRequirement {
	req, err := NewFilterHoldRequirementNone(
		f.channel,
		[]EventKind{EventKindTextDelta, EventKindReasoningDelta, EventKindToolCallFragment},
	)
	if err != nil {
		// Fallback to text delta if kind combination rejected.
		req, _ = NewFilterHoldRequirementNone(f.channel, []EventKind{EventKindTextDelta})
	}
	return req
}

// Evaluate evaluates the evidence batch and returns a FilterDecision of kind pass.
func (f NoopFilter) Evaluate(ctx context.Context, fctx FilterContext, batch EvidenceBatch) (FilterDecision, error) {
	if err := ctx.Err(); err != nil {
		return FilterDecision{}, err
	}

	events := batch.Events()
	evKind := EventKindTextDelta
	if len(events) > 0 {
		evKind = events[len(events)-1].Kind()
	}

	fp := FixedFingerprint{0x01}
	ts := batch.CapturedAt()
	if ts.IsZero() {
		return FilterDecision{}, errors.New("streamgate: noop filter batch captured timestamp is zero")
	}

	evidence, err := NewSanitizedEvidence(
		evKind,
		f.channel,
		DefaultNoopRuleID,
		DefaultNoopDescriptorCode,
		fp,
		len(events),
		0,
		FilterOutcomeKindEvaluated,
		ts,
	)
	if err != nil {
		return FilterDecision{}, err
	}

	return NewFilterDecision(
		FilterDecisionKindPass,
		DefaultNoopConsumerID,
		f.ID(),
		DefaultNoopRuleID,
		evidence,
		nil,
	)
}
