package teamspeak

import (
	"testing"
	"time"
)

func TestResetForConnectKeepsEventLoopAlive(t *testing.T) {
	c := newTestClient(t)
	voice := make(chan VoiceDataEvent, 1)
	c.OnVoiceData(func(event VoiceDataEvent) { voice <- event })

	c.mu.Lock()
	c.resetForConnectLocked()
	c.mu.Unlock()
	c.notifyEvent(VoiceDataEvent{ClientID: 7, Sequence: 11, Data: []byte{1}})

	select {
	case event := <-voice:
		if event.ClientID != 7 || event.Sequence != 11 {
			t.Fatalf("unexpected voice event: %+v", event)
		}
	case <-time.After(time.Second):
		t.Fatal("event loop stopped during connection reset")
	}
}

func TestOnTextMessage_RegistersHandler(t *testing.T) {
	c := newTestClient(t)

	called := make(chan TextMessage, 1)
	c.OnTextMessage(func(m TextMessage) { called <- m })
	c.rebuildMiddlewareChains()

	c.finalEvtHandler(TextMessage{Message: "hi"})

	select {
	case m := <-called:
		if m.Message != "hi" {
			t.Errorf("expected 'hi', got %q", m.Message)
		}
	case <-time.After(time.Second):
		t.Error("OnTextMessage handler not called")
	}
}

func TestOnClientLeave_RegistersHandler(t *testing.T) {
	c := newTestClient(t)

	called := make(chan ClientLeftViewEvent, 1)
	c.OnClientLeave(func(e ClientLeftViewEvent) { called <- e })
	c.rebuildMiddlewareChains()

	c.finalEvtHandler(ClientLeftViewEvent{ID: 5})

	select {
	case e := <-called:
		if e.ID != 5 {
			t.Errorf("expected ID=5, got %d", e.ID)
		}
	case <-time.After(time.Second):
		t.Error("OnClientLeave handler not called")
	}
}

func TestOnDisconnected_RegistersHandler(t *testing.T) {
	c := newTestClient(t)

	called := make(chan error, 1)
	c.OnDisconnected(func(err error) { called <- err })

	// OnDisconnected handlers are called by onClosed; fire it directly.
	c.mu.Lock()
	handlers := c.disconnectedHandlers
	c.mu.Unlock()

	for _, h := range handlers {
		go h(nil)
	}

	select {
	case <-called:
	case <-time.After(time.Second):
		t.Error("OnDisconnected handler not called")
	}
}

func TestMultipleHandlers_AllCalled(t *testing.T) {
	c := newTestClient(t)

	a := make(chan struct{}, 1)
	b := make(chan struct{}, 1)
	c.OnTextMessage(func(_ TextMessage) { a <- struct{}{} })
	c.OnTextMessage(func(_ TextMessage) { b <- struct{}{} })
	c.rebuildMiddlewareChains()

	c.finalEvtHandler(TextMessage{Message: "test"})

	for _, ch := range []chan struct{}{a, b} {
		select {
		case <-ch:
		case <-time.After(time.Second):
			t.Error("not all handlers called")
		}
	}
}

func TestUseCommandMiddleware_InterceptsCommands(t *testing.T) {
	c := newTestClient(t)

	intercepted := make(chan string, 1)
	c.UseCommandMiddleware(func(next func(string) error) func(string) error {
		return func(cmd string) error {
			intercepted <- cmd

			return next(cmd)
		}
	})

	err := c.finalCmdHandler("test cmd")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	select {
	case cmd := <-intercepted:
		if cmd != "test cmd" {
			t.Errorf("expected 'test cmd', got %q", cmd)
		}
	case <-time.After(time.Second):
		t.Error("middleware not called")
	}
}

func TestUseCommandMiddleware_ChainOrder(t *testing.T) {
	c := newTestClient(t)

	var order []string
	c.UseCommandMiddleware(
		func(next func(string) error) func(string) error {
			return func(cmd string) error {
				order = append(order, "first")

				return next(cmd)
			}
		},
		func(next func(string) error) func(string) error {
			return func(cmd string) error {
				order = append(order, "second")

				return next(cmd)
			}
		},
	)

	_ = c.finalCmdHandler("x")

	if len(order) != 2 || order[0] != "first" || order[1] != "second" {
		t.Errorf("unexpected middleware order: %v", order)
	}
}

func TestUseCommandMiddleware_CanShortCircuit(t *testing.T) {
	c := newTestClient(t)

	sent := make(chan string, 1)
	c.UseCommandMiddleware(func(next func(string) error) func(string) error {
		return func(cmd string) error {
			if cmd == "blocked" {
				return nil // don't call next
			}

			return next(cmd)
		}
	})

	// Wrap the base handler to detect if it was called.
	origBase := c.finalCmdHandler
	c.finalCmdHandler = func(cmd string) error {
		sent <- cmd

		return origBase(cmd)
	}
	// Re-apply middleware on top of new base.
	c.UseCommandMiddleware()

	_ = c.SendCommandNoWait("blocked")

	select {
	case <-sent:
		t.Error("short-circuited command should not reach base handler")
	case <-time.After(50 * time.Millisecond):
	}
}

func TestUseEventMiddleware_InterceptsEvents(t *testing.T) {
	c := newTestClient(t)

	intercepted := make(chan any, 1)
	c.UseEventMiddleware(func(next func(any)) func(any) {
		return func(evt any) {
			intercepted <- evt
			next(evt)
		}
	})

	c.finalEvtHandler(TextMessage{Message: "intercepted"})

	select {
	case evt := <-intercepted:
		if m, ok := evt.(TextMessage); !ok || m.Message != "intercepted" {
			t.Errorf("unexpected event: %v", evt)
		}
	case <-time.After(time.Second):
		t.Error("event middleware not called")
	}
}

func TestUseEventMiddleware_CanFilter(t *testing.T) {
	c := newTestClient(t)

	reached := make(chan TextMessage, 1)
	c.OnTextMessage(func(m TextMessage) { reached <- m })

	// Filter: block all text messages.
	c.UseEventMiddleware(func(next func(any)) func(any) {
		return func(evt any) {
			if _, ok := evt.(TextMessage); ok {
				return // drop
			}
			next(evt)
		}
	})

	c.finalEvtHandler(TextMessage{Message: "filtered"})

	select {
	case <-reached:
		t.Error("filtered event should not reach handler")
	case <-time.After(50 * time.Millisecond):
	}
}

func TestFinalEvtHandler_UnknownType_NoPanic(t *testing.T) {
	c := newTestClient(t)
	// Should not panic for an unhandled event type.
	c.finalEvtHandler(struct{ X int }{X: 42})
}
