package jobs

import (
	"context"
	"encoding/json"
	"errors"
	"testing"
)

func TestRunnerDispatchesRegisteredHandler(t *testing.T) {
	runner := NewRunner()
	called := false
	var receivedPayload json.RawMessage

	testKind := Kind("test_job")
	runner.Register(testKind, func(ctx context.Context, payload json.RawMessage) error {
		called = true
		receivedPayload = payload
		return nil
	})

	job := Job{
		ID:      "job-1",
		Kind:    testKind,
		Payload: json.RawMessage(`{"key":"value"}`),
	}

	err := runner.Execute(context.Background(), job)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if !called {
		t.Error("expected handler to be called, but it was not")
	}

	if string(receivedPayload) != `{"key":"value"}` {
		t.Errorf("expected payload %q, got %q", `{"key":"value"}`, string(receivedPayload))
	}
}

func TestRunnerRejectsUnknownKind(t *testing.T) {
	runner := NewRunner()
	job := Job{
		ID:   "job-2",
		Kind: Kind("non_existent"),
	}

	err := runner.Execute(context.Background(), job)
	if err == nil {
		t.Fatal("expected error for unknown job kind, got nil")
	}
}

func TestRunnerRespectsCanceledContext(t *testing.T) {
	runner := NewRunner()
	testKind := Kind("cancellation_test")

	runner.Register(testKind, func(ctx context.Context, payload json.RawMessage) error {
		return nil
	})

	ctx, cancel := context.WithCancel(context.Background())
	cancel() // Cancel context immediately

	job := Job{
		ID:   "job-3",
		Kind: testKind,
	}

	err := runner.Execute(ctx, job)
	if err == nil {
		t.Fatal("expected error due to canceled context, got nil")
	}
	if !errors.Is(err, context.Canceled) {
		t.Errorf("expected error %v, got %v", context.Canceled, err)
	}
}

func TestRunnerHandlesPanic(t *testing.T) {
	runner := NewRunner()
	panicKind := Kind("panic_job")

	runner.Register(panicKind, func(ctx context.Context, payload json.RawMessage) error {
		panic("something went critically wrong")
	})

	job := Job{
		ID:   "job-4",
		Kind: panicKind,
	}

	err := runner.Execute(context.Background(), job)
	if err == nil {
		t.Fatal("expected error from panic recovery, got nil")
	}
	if err.Error() != "job panicked: something went critically wrong" {
		t.Errorf("unexpected error message: %v", err)
	}
}

func TestRegisterBuiltins(t *testing.T) {
	runner := NewRunner()
	RegisterBuiltins(runner)

	expectedCount := 3
	if runner.Len() != expectedCount {
		t.Errorf("expected %d registered built-in handlers, got %d", expectedCount, runner.Len())
	}
}
