package filters

import (
	"context"
	"errors"
	"testing"
)

// ── Mock filter ─────────────────────────────

type mockFilter struct {
	name     string
	order    int
	messages []InjectedMessage
	err      error
}

func (m *mockFilter) Name() string { return m.name }
func (m *mockFilter) Order() int   { return m.order }
func (m *mockFilter) Execute(_ context.Context, _ *CompletionContext) (*Contribution, error) {
	if m.err != nil {
		return nil, m.err
	}
	if len(m.messages) == 0 {
		return nil, nil
	}
	return &Contribution{Messages: m.messages}, nil
}

// ── Tests ───────────────────────────────────

func TestChainEmpty(t *testing.T) {
	c := NewChain()
	result := c.Execute(context.Background(), &CompletionContext{})
	if len(result) != 0 {
		t.Fatalf("expected 0 messages, got %d", len(result))
	}
}

func TestChainSingleFilter(t *testing.T) {
	c := NewChain()
	c.Register(&mockFilter{
		name:  "test",
		order: 0,
		messages: []InjectedMessage{
			{Role: "system", Content: "hello"},
		},
	})

	result := c.Execute(context.Background(), &CompletionContext{
		ChannelID: "ch-1",
		UserID:    "u-1",
	})

	if len(result) != 1 {
		t.Fatalf("expected 1 message, got %d", len(result))
	}
	if result[0].Content != "hello" {
		t.Fatalf("expected 'hello', got %q", result[0].Content)
	}
}

func TestChainOrderRespected(t *testing.T) {
	c := NewChain()
	// Register out of order — chain should sort by Order()
	c.Register(&mockFilter{
		name:     "second",
		order:    20,
		messages: []InjectedMessage{{Role: "system", Content: "B"}},
	})
	c.Register(&mockFilter{
		name:     "first",
		order:    10,
		messages: []InjectedMessage{{Role: "system", Content: "A"}},
	})

	result := c.Execute(context.Background(), &CompletionContext{})

	if len(result) != 2 {
		t.Fatalf("expected 2 messages, got %d", len(result))
	}
	if result[0].Content != "A" {
		t.Fatalf("expected 'A' first, got %q", result[0].Content)
	}
	if result[1].Content != "B" {
		t.Fatalf("expected 'B' second, got %q", result[1].Content)
	}
}

func TestChainErrorIsolation(t *testing.T) {
	c := NewChain()
	c.Register(&mockFilter{
		name:  "failing",
		order: 0,
		err:   errors.New("boom"),
	})
	c.Register(&mockFilter{
		name:     "surviving",
		order:    10,
		messages: []InjectedMessage{{Role: "system", Content: "ok"}},
	})

	result := c.Execute(context.Background(), &CompletionContext{})

	if len(result) != 1 {
		t.Fatalf("expected 1 message (failing filter skipped), got %d", len(result))
	}
	if result[0].Content != "ok" {
		t.Fatalf("expected 'ok', got %q", result[0].Content)
	}
}

func TestChainNilContribution(t *testing.T) {
	c := NewChain()
	c.Register(&mockFilter{
		name:  "noop",
		order: 0,
		// nil messages → nil contribution
	})

	result := c.Execute(context.Background(), &CompletionContext{})
	if len(result) != 0 {
		t.Fatalf("expected 0 messages from noop filter, got %d", len(result))
	}
}

func TestChainMultipleMessages(t *testing.T) {
	c := NewChain()
	c.Register(&mockFilter{
		name:  "multi",
		order: 0,
		messages: []InjectedMessage{
			{Role: "system", Content: "first"},
			{Role: "system", Content: "second"},
		},
	})

	result := c.Execute(context.Background(), &CompletionContext{})

	if len(result) != 2 {
		t.Fatalf("expected 2 messages, got %d", len(result))
	}
}

func TestChainLen(t *testing.T) {
	c := NewChain()
	if c.Len() != 0 {
		t.Fatalf("expected 0, got %d", c.Len())
	}
	c.Register(&mockFilter{name: "a", order: 0})
	c.Register(&mockFilter{name: "b", order: 1})
	if c.Len() != 2 {
		t.Fatalf("expected 2, got %d", c.Len())
	}
}
