package worker_test

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"git.toki-labs.com/toki/gito/services/core/internal/worker"
)

func TestHTTPInvokerSuccess(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost || r.URL.Path != "/invoke" {
			t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path)
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		_ = json.NewEncoder(w).Encode(map[string]any{"state": "succeeded", "message": "done"})
	}))
	defer srv.Close()

	invoker := worker.NewHTTPInvoker(srv.URL)
	result, err := invoker.InvokeAgentRun(context.Background(), worker.AgentRunInput{
		OperationID:   "op-1",
		RepoID:        "repo-1",
		Branch:        "main",
		WorkspacePath: "/ws",
		Instruction:   "run linter",
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.State != worker.AgentRunStateSucceeded {
		t.Errorf("state: got %q want succeeded", result.State)
	}
	if result.Message != "done" {
		t.Errorf("message: got %q want done", result.Message)
	}
}

func TestHTTPInvokerFailedState(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		_ = json.NewEncoder(w).Encode(map[string]any{"state": "failed", "message": "lint failed"})
	}))
	defer srv.Close()

	invoker := worker.NewHTTPInvoker(srv.URL)
	result, err := invoker.InvokeAgentRun(context.Background(), worker.AgentRunInput{OperationID: "op-2"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.State != worker.AgentRunStateFailed {
		t.Errorf("state: got %q want failed", result.State)
	}
}

func TestHTTPInvokerCancelledState(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		_ = json.NewEncoder(w).Encode(map[string]any{"state": "cancelled"})
	}))
	defer srv.Close()

	invoker := worker.NewHTTPInvoker(srv.URL)
	result, err := invoker.InvokeAgentRun(context.Background(), worker.AgentRunInput{OperationID: "op-3"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.State != worker.AgentRunStateCancelled {
		t.Errorf("state: got %q want cancelled", result.State)
	}
}

func TestHTTPInvokerTransportError(t *testing.T) {
	// Closed server simulates transport error.
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
	srv.Close()

	invoker := worker.NewHTTPInvoker(srv.URL)
	result, err := invoker.InvokeAgentRun(context.Background(), worker.AgentRunInput{OperationID: "op-4"})
	if err != nil {
		t.Fatalf("unexpected error (transport error should return sanitized result, not error): %v", err)
	}
	if result.State != worker.AgentRunStateFailed {
		t.Errorf("transport error state: got %q want failed", result.State)
	}
	if strings.Contains(result.Message, "connection refused") || strings.Contains(result.Message, "dial tcp") {
		t.Errorf("transport error message must not contain raw upstream details: %q", result.Message)
	}
}

func TestHTTPInvokerNonSuccessStatusCode(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
		_, _ = io.WriteString(w, `{"error":"internal server error","trace":"..."}`)
	}))
	defer srv.Close()

	invoker := worker.NewHTTPInvoker(srv.URL)
	result, err := invoker.InvokeAgentRun(context.Background(), worker.AgentRunInput{OperationID: "op-5"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.State != worker.AgentRunStateFailed {
		t.Errorf("non-2xx state: got %q want failed", result.State)
	}
	if strings.Contains(result.Message, "internal server error") || strings.Contains(result.Message, "trace") {
		t.Errorf("non-2xx message must not expose upstream body: %q", result.Message)
	}
}

func TestHTTPInvokerInvalidResponseBody(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		_, _ = io.WriteString(w, `not-json`)
	}))
	defer srv.Close()

	invoker := worker.NewHTTPInvoker(srv.URL)
	result, err := invoker.InvokeAgentRun(context.Background(), worker.AgentRunInput{OperationID: "op-6"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.State != worker.AgentRunStateFailed {
		t.Errorf("invalid response state: got %q want failed", result.State)
	}
}

func TestHTTPInvokerUnsupportedState(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		_ = json.NewEncoder(w).Encode(map[string]any{"state": "unknown_state"})
	}))
	defer srv.Close()

	invoker := worker.NewHTTPInvoker(srv.URL)
	result, err := invoker.InvokeAgentRun(context.Background(), worker.AgentRunInput{OperationID: "op-7"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.State != worker.AgentRunStateFailed {
		t.Errorf("unsupported state result: got %q want failed", result.State)
	}
}
