package logscanner

import (
	"errors"
	"strings"
	"testing"

	"github.com/alexei/tinyforge/internal/store"
)

// fakeRuleSource lets us inject rules into ReloadRules without
// standing up SQLite. Mirrors the fakeTriggerSource pattern from
// the events package.
type fakeRuleSource struct {
	rows []store.LogScanRule
	err  error
}

func (f *fakeRuleSource) ListLogScanRules() ([]store.LogScanRule, error) {
	if f.err != nil {
		return nil, f.err
	}
	return f.rows, nil
}

func TestManagerStats_CapturesCompileErrors(t *testing.T) {
	rs := &fakeRuleSource{rows: []store.LogScanRule{
		{ID: 1, Name: "valid", Pattern: `panic`, Severity: "warn", Streams: "all", Enabled: true},
		{ID: 2, Name: "broken", Pattern: `([unclosed`, Severity: "warn", Streams: "all", Enabled: true},
		{ID: 3, Name: "also-broken", Pattern: `[`, Severity: "warn", Streams: "all", Enabled: true},
	}}
	m := NewManager(Config{Rules: rs})
	if err := m.ReloadRules(); err != nil {
		t.Fatalf("ReloadRules: %v", err)
	}
	stats := m.Stats()
	if len(stats.LastCompileErrors) != 2 {
		t.Fatalf("expected 2 compile errors, got %d: %+v", len(stats.LastCompileErrors), stats.LastCompileErrors)
	}
	// The error messages should mention the rule id/name from the
	// BuildSnapshot format so operators can find which rule broke.
	joined := strings.Join(stats.LastCompileErrors, "|")
	if !strings.Contains(joined, "broken") {
		t.Errorf("error messages should reference rule name 'broken': %s", joined)
	}
}

func TestManagerStats_CompileErrorsReplacedOnReload(t *testing.T) {
	// A broken rule then a reload with all-valid rules should
	// clear the error list — operators expect the panel to flip
	// from "2 errors" to "all clean" after they fix things.
	rs := &fakeRuleSource{rows: []store.LogScanRule{
		{ID: 1, Name: "broken", Pattern: `([`, Severity: "warn", Streams: "all", Enabled: true},
	}}
	m := NewManager(Config{Rules: rs})
	_ = m.ReloadRules()
	if len(m.Stats().LastCompileErrors) != 1 {
		t.Fatal("expected one compile error before fix")
	}

	rs.rows = []store.LogScanRule{
		{ID: 1, Name: "fixed", Pattern: `panic`, Severity: "warn", Streams: "all", Enabled: true},
	}
	_ = m.ReloadRules()
	if len(m.Stats().LastCompileErrors) != 0 {
		t.Errorf("expected zero compile errors after reload, got %d",
			len(m.Stats().LastCompileErrors))
	}
}

func TestManagerStats_ReloadErrorPropagates(t *testing.T) {
	rs := &fakeRuleSource{err: errors.New("db down")}
	m := NewManager(Config{Rules: rs})
	if err := m.ReloadRules(); err == nil {
		t.Fatal("expected ReloadRules to propagate the source error")
	}
}

func TestManagerStats_ActiveTailsDefaultsZero(t *testing.T) {
	// Without Start() and without a docker dependency we can't
	// run real tails, but the counter should be a stable 0 read
	// rather than panic/uninitialized.
	rs := &fakeRuleSource{}
	m := NewManager(Config{Rules: rs})
	if got := m.Stats().ActiveTails; got != 0 {
		t.Errorf("ActiveTails on fresh manager = %d, want 0", got)
	}
}
