package webhooks

import (
	"context"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/rs/zerolog"

	"github.com/operator/command-center/internal/eventbus"
)

func TestAutobrrHandlerPublishes(t *testing.T) {
	bus := eventbus.New(zerolog.Nop())
	sub := bus.Subscribe(eventbus.TopicAutobrrGrab)
	defer sub.Close()

	h := &AutobrrHandler{Bus: bus}
	body := []byte(`{
		"event_id": "abc",
		"filter_id": "42",
		"filter_name": "1080p Internal",
		"release_name": "Some.Release.2024.1080p.WEB.DL",
		"indexer": "TestTracker",
		"info_hash": "deadbeef",
		"size": 1234567
	}`)
	r := httptest.NewRequest("POST", "/", nil)
	if err := h.Handle(context.Background(), r, body, "ep1"); err != nil {
		t.Fatalf("Handle: %v", err)
	}
	select {
	case ev := <-sub.Chan():
		got := ev.Payload.(AutobrrGrabEvent)
		if got.ReleaseName != "Some.Release.2024.1080p.WEB.DL" {
			t.Errorf("release name: %q", got.ReleaseName)
		}
		if got.InfoHash != "deadbeef" {
			t.Errorf("info hash: %q", got.InfoHash)
		}
		if got.FilterID != "42" {
			t.Errorf("filter id: %q", got.FilterID)
		}
		if got.EndpointID != "ep1" {
			t.Errorf("endpoint: %q", got.EndpointID)
		}
	case <-time.After(time.Second):
		t.Fatal("event not published")
	}
}

func TestQbitHandlerPublishesCompletedAndTorrents(t *testing.T) {
	bus := eventbus.New(zerolog.Nop())
	completedSub := bus.Subscribe(eventbus.TopicTorrentCompleted)
	torrentsSub := bus.Subscribe(eventbus.TopicTorrents)
	defer completedSub.Close()
	defer torrentsSub.Close()

	h := &QbitHandler{Bus: bus}
	body := []byte(`{
		"kind": "completed",
		"info_hash": "abc123",
		"name": "Test",
		"state": "uploading",
		"category": "movies"
	}`)
	r := httptest.NewRequest("POST", "/", nil)
	if err := h.Handle(context.Background(), r, body, "qbit-ep"); err != nil {
		t.Fatalf("Handle: %v", err)
	}

	if _, ok := tryRecv(completedSub.Chan()); !ok {
		t.Error("torrent.completed event missing")
	}
	if _, ok := tryRecv(torrentsSub.Chan()); !ok {
		t.Error("torrents fan-out event missing")
	}
}

func TestCrossseedHandlerEventVariants(t *testing.T) {
	bus := eventbus.New(zerolog.Nop())
	found := bus.Subscribe(eventbus.TopicCrossseedMatchFound)
	applied := bus.Subscribe(eventbus.TopicCrossseedMatchApplied)
	defer found.Close()
	defer applied.Close()

	h := &CrossseedHandler{Bus: bus}
	r := httptest.NewRequest("POST", "/", nil)
	if err := h.Handle(context.Background(), r, []byte(`{
		"event": "MATCH",
		"releaseName": "X",
		"infoHash": "abc",
		"destTracker": "TrackerA"
	}`), "cs"); err != nil {
		t.Fatalf("Handle MATCH: %v", err)
	}
	if _, ok := tryRecv(found.Chan()); !ok {
		t.Error("match_found event missing")
	}

	if err := h.Handle(context.Background(), r, []byte(`{
		"event": "INJECTED",
		"name": "Y",
		"torrent_hash": "def"
	}`), "cs"); err != nil {
		t.Fatalf("Handle INJECTED: %v", err)
	}
	if _, ok := tryRecv(applied.Chan()); !ok {
		t.Error("match_applied event missing")
	}
}

func TestGenericHandlerPublishesRaw(t *testing.T) {
	bus := eventbus.New(zerolog.Nop())
	sub := bus.Subscribe(eventbus.TopicWebhookCustom)
	defer sub.Close()

	h := &GenericHandler{Bus: bus}
	r := httptest.NewRequest("POST", "/", nil)
	if err := h.Handle(context.Background(), r, []byte(`{"anything":"goes","n":42}`), "g"); err != nil {
		t.Fatalf("Handle: %v", err)
	}
	ev, ok := tryRecv(sub.Chan())
	if !ok {
		t.Fatal("event not published")
	}
	got := ev.Payload.(CustomEvent)
	if got.Payload["anything"] != "goes" {
		t.Errorf("payload: %+v", got.Payload)
	}
	if got.EndpointID != "g" {
		t.Errorf("endpoint: %q", got.EndpointID)
	}
}

func TestGenericHandlerHandlesNonJSON(t *testing.T) {
	bus := eventbus.New(zerolog.Nop())
	sub := bus.Subscribe(eventbus.TopicWebhookCustom)
	defer sub.Close()

	h := &GenericHandler{Bus: bus}
	r := httptest.NewRequest("POST", "/", nil)
	if err := h.Handle(context.Background(), r, []byte("not json at all"), "g"); err != nil {
		t.Fatalf("Handle: %v", err)
	}
	ev, ok := tryRecv(sub.Chan())
	if !ok {
		t.Fatal("event not published")
	}
	got := ev.Payload.(CustomEvent)
	if got.Payload["raw"] != "not json at all" {
		t.Errorf("expected raw field, got %+v", got.Payload)
	}
}

func tryRecv(ch <-chan eventbus.Event) (eventbus.Event, bool) {
	select {
	case ev := <-ch:
		return ev, true
	case <-time.After(500 * time.Millisecond):
		return eventbus.Event{}, false
	}
}
