package tools

import (
	"testing"

	"skraak/utils"
)

func NewClassifyState(config ClassifyConfig, dataFiles []*utils.DataFile) *ClassifyState {
	hasFilter := config.Filter != "" || config.Species != "" || config.Certainty >= 0
	cached := make([][]*utils.Segment, len(dataFiles))
	for i, df := range dataFiles {
		if !hasFilter {
			cached[i] = df.Segments
		} else {
			for _, seg := range df.Segments {
				if seg.SegmentMatchesFilters(config.Filter, config.Species, config.CallType, config.Certainty) {
					cached[i] = append(cached[i], seg)
				}
			}
		}
	}
	total := 0
	for _, segs := range cached {
		total += len(segs)
	}
	return &ClassifyState{
		Config:       config,
		DataFiles:    dataFiles,
		filteredSegs: cached,
		totalSegs:    total,
	}
}

func TestParseKeyBuffer(t *testing.T) {
	bindings := []KeyBinding{
		{Key: "k", Species: "Kiwi"},
		{Key: "d", Species: "Kiwi", CallType: "Duet"},
		{Key: "n", Species: "Don't Know"},
		{Key: "p", Species: "Morepork"},
	}

	state := NewClassifyState(ClassifyConfig{Bindings: bindings, Certainty: -1}, nil)

	tests := []struct {
		key     string
		want    *BindingResult
		wantNil bool
	}{
		{"k", &BindingResult{Species: "Kiwi"}, false},
		{"d", &BindingResult{Species: "Kiwi", CallType: "Duet"}, false},
		{"n", &BindingResult{Species: "Don't Know"}, false},
		{"p", &BindingResult{Species: "Morepork"}, false},
		{"x", nil, true}, // unknown key
	}

	for _, tt := range tests {
		got := state.ParseKeyBuffer(tt.key)
		if tt.wantNil {
			if got != nil {
				t.Errorf("ParseKeyBuffer(%q) = %v, want nil", tt.key, got)
			}
		} else {
			if got == nil {
				t.Errorf("ParseKeyBuffer(%q) = nil, want %+v", tt.key, tt.want)
				continue
			}
			if got.Species != tt.want.Species {
				t.Errorf("ParseKeyBuffer(%q).Species = %q, want %q", tt.key, got.Species, tt.want.Species)
			}
			if got.CallType != tt.want.CallType {
				t.Errorf("ParseKeyBuffer(%q).CallType = %q, want %q", tt.key, got.CallType, tt.want.CallType)
			}
		}
	}
}

func TestApplyBinding(t *testing.T) {
	bindings := []KeyBinding{
		{Key: "k", Species: "Kiwi"},
		{Key: "n", Species: "Don't Know"},
		{Key: "d", Species: "Kiwi", CallType: "Duet"},
	}

	df := &utils.DataFile{
		Meta: &utils.DataMeta{},
		Segments: []*utils.Segment{
			{
				StartTime: 10.0,
				EndTime:   20.0,
				Labels: []*utils.Label{
					{Species: "Unknown", Certainty: 50, Filter: "test-filter", CallType: "OldType"},
				},
			},
		},
	}

	state := NewClassifyState(ClassifyConfig{
		Filter:    "test-filter",
		Reviewer:  "David",
		Bindings:  bindings,
		Certainty: -1,
	}, []*utils.DataFile{df})

	// Apply "k" = Kiwi (no calltype, should remove existing calltype)
	result := &BindingResult{Species: "Kiwi"}
	state.ApplyBinding(result)

	// Check label was updated
	if len(df.Segments[0].Labels) != 1 {
		t.Errorf("expected 1 label, got %d", len(df.Segments[0].Labels))
	}
	if df.Segments[0].Labels[0].Species != "Kiwi" {
		t.Errorf("expected Species=Kiwi, got %s", df.Segments[0].Labels[0].Species)
	}
	if df.Segments[0].Labels[0].Certainty != 100 {
		t.Errorf("expected Certainty=100, got %d", df.Segments[0].Labels[0].Certainty)
	}
	if df.Segments[0].Labels[0].CallType != "" {
		t.Errorf("expected CallType='', got %s (should be removed)", df.Segments[0].Labels[0].CallType)
	}
	if df.Meta.Reviewer != "David" {
		t.Errorf("expected Reviewer=David, got %s", df.Meta.Reviewer)
	}

	// Apply "d" = Kiwi/Duet (should set calltype)
	result = &BindingResult{Species: "Kiwi", CallType: "Duet"}
	state.ApplyBinding(result)

	if df.Segments[0].Labels[0].CallType != "Duet" {
		t.Errorf("expected CallType=Duet, got %s", df.Segments[0].Labels[0].CallType)
	}

	// Apply "n" = Don't Know (certainty should be 0)
	result = &BindingResult{Species: "Don't Know"}
	state.ApplyBinding(result)

	if df.Segments[0].Labels[0].Species != "Don't Know" {
		t.Errorf("expected Species=Don't Know, got %s", df.Segments[0].Labels[0].Species)
	}
	if df.Segments[0].Labels[0].Certainty != 0 {
		t.Errorf("expected Certainty=0 for Don't Know, got %d", df.Segments[0].Labels[0].Certainty)
	}
}

func TestApplyBindingCallTypeRemoval(t *testing.T) {
	bindings := []KeyBinding{
		{Key: "k", Species: "Kiwi"}, // no calltype
	}

	df := &utils.DataFile{
		Meta: &utils.DataMeta{},
		Segments: []*utils.Segment{
			{
				StartTime: 10.0,
				EndTime:   20.0,
				Labels: []*utils.Label{
					{Species: "Kiwi", Certainty: 100, Filter: "test-filter", CallType: "Male"},
				},
			},
		},
	}

	state := NewClassifyState(ClassifyConfig{
		Filter:    "test-filter",
		Reviewer:  "David",
		Bindings:  bindings,
		Certainty: -1,
	}, []*utils.DataFile{df})

	// Apply "k" = Kiwi (should remove Male calltype)
	result := &BindingResult{Species: "Kiwi"}
	state.ApplyBinding(result)

	if df.Segments[0].Labels[0].CallType != "" {
		t.Errorf("expected CallType='', got %s (should be removed)", df.Segments[0].Labels[0].CallType)
	}
}

func TestConfirmLabelDontKnow(t *testing.T) {
	df := &utils.DataFile{
		Meta: &utils.DataMeta{},
		Segments: []*utils.Segment{
			{
				StartTime: 10.0,
				EndTime:   20.0,
				Labels: []*utils.Label{
					{Species: "Don't Know", Certainty: 0, Filter: "test-filter"},
				},
			},
		},
	}

	state := NewClassifyState(ClassifyConfig{
		Filter:    "test-filter",
		Reviewer:  "David",
		Certainty: -1,
	}, []*utils.DataFile{df})

	// ConfirmLabel on Don't Know should be a no-op
	if state.ConfirmLabel() {
		t.Error("ConfirmLabel() should return false for Don't Know (certainty=0)")
	}

	label := df.Segments[0].Labels[0]
	if label.Species != "Don't Know" {
		t.Errorf("Species should remain Don't Know, got %s", label.Species)
	}
	if label.Certainty != 0 {
		t.Errorf("Certainty should remain 0, got %d", label.Certainty)
	}
	if state.Dirty {
		t.Error("State should not be dirty after confirming Don't Know")
	}
}