package exec

import (
	"context"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/wire/apiclient"
)

func TestApiclientScheduledMessageMutations(t *testing.T) {
	var method, path, body, auth, idempotency, requestID string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		method, path, auth = r.Method, r.URL.Path, r.Header.Get("Authorization")
		idempotency, requestID = r.Header.Get("Idempotency-Key"), r.Header.Get("X-Request-ID")
		if r.Body != nil {
			b, _ := io.ReadAll(r.Body)
			body = string(b)
		}
		switch r.Method {
		case "POST":
			if strings.HasSuffix(path, "/pause") || strings.HasSuffix(path, "/resume") {
				io.WriteString(w, `{"id":"sch_1","status":"changed","version":2}`)
			} else {
				io.WriteString(w, `{"id":"sch_new","name":"N"}`)
			}
		case "PATCH":
			io.WriteString(w, `{"id":"sch_1","name":"updated"}`)
		case "DELETE":
			w.WriteHeader(http.StatusNoContent)
		}
	}))
	defer srv.Close()
	api := scheduledMessageRemote{client: &apiclient.Client{ServerURL: srv.URL, AgentID: "ag", AuthToken: "tok", RouteWorkspaceID: "ws", HTTPClient: srv.Client()}}

	t.Run("receipt-backed create and update forward selectors", func(t *testing.T) {
		_, err := api.create(context.Background(), scheduledMessageCreateRequest{
			scheduledMessageWriteRequest: validScheduledMessageWriteRequest("receipt"), IdempotencyKey: "idem-create",
		})
		require.NoError(t, err)
		require.Equal(t, "idem-create", idempotency)
		require.Equal(t, "idem-create", requestID)
		_, err = api.update(context.Background(), "sch_1", scheduledMessageUpdateRequest{
			scheduledMessageWriteRequest: validScheduledMessageWriteRequest("receipt-update"), IdempotencyKey: "idem-update",
		})
		require.NoError(t, err)
		require.Equal(t, "idem-update", idempotency)
		require.Equal(t, "idem-update", requestID)
	})

	t.Run("create POST /schedules with Bearer", func(t *testing.T) {
		method, path, body, auth, idempotency, requestID = "", "", "", "", "", ""
		raw, err := api.create(context.Background(), scheduledMessageCreateRequest{scheduledMessageWriteRequest: validScheduledMessageWriteRequest("N")})
		if err != nil {
			t.Fatal(err)
		}
		if method != http.MethodPost || path != "/api/v1/workspaces/ws/agents/ag/runtime/schedules" {
			t.Errorf("request: %s %s", method, path)
		}
		if auth != "Bearer tok" {
			t.Errorf("bearer: %q", auth)
		}
		if !strings.Contains(body, `"name":"N"`) {
			t.Errorf("body: %s", body)
		}
		if raw.ID != "sch_new" {
			t.Errorf("result: %+v", raw)
		}
	})
	t.Run("update PATCH /schedules/<id> with Bearer", func(t *testing.T) {
		method, path, body, auth, idempotency, requestID = "", "", "", "", "", ""
		_, err := api.update(context.Background(), "sch_1", scheduledMessageUpdateRequest{scheduledMessageWriteRequest: validScheduledMessageWriteRequest("N2")})
		if err != nil {
			t.Fatal(err)
		}
		if method != http.MethodPatch || path != "/api/v1/workspaces/ws/agents/ag/runtime/schedules/sch_1" {
			t.Errorf("request: %s %s", method, path)
		}
		if auth != "Bearer tok" {
			t.Errorf("bearer: %q", auth)
		}
	})
	t.Run("pause POST with {} body and Bearer", func(t *testing.T) {
		method, path, body, auth = "", "", "", ""
		raw, err := api.pause(context.Background(), "sch_1")
		if err != nil {
			t.Fatal(err)
		}
		if method != http.MethodPost || path != "/api/v1/workspaces/ws/agents/ag/runtime/schedules/sch_1/pause" {
			t.Errorf("req: %s %s", method, path)
		}
		if body != "{}" {
			t.Errorf("body: %q, want {}", body)
		}
		if auth != "Bearer tok" {
			t.Errorf("bearer: %q", auth)
		}
		if raw.ID != "sch_1" || raw.Status != "changed" {
			t.Errorf("result must include schedule fields: %+v", raw)
		}
	})
	t.Run("resume POST with {} body", func(t *testing.T) {
		method, path, body = "", "", ""
		_, err := api.resume(context.Background(), "sch_1")
		if err != nil {
			t.Fatal(err)
		}
		if method != http.MethodPost || path != "/api/v1/workspaces/ws/agents/ag/runtime/schedules/sch_1/resume" {
			t.Errorf("req: %s %s", method, path)
		}
		if body != "{}" {
			t.Errorf("body: %q, want {}", body)
		}
	})
	t.Run("delete DELETE /schedules/<id>", func(t *testing.T) {
		method, path = "", ""
		if err := api.delete(context.Background(), "sch_1"); err != nil {
			t.Fatal(err)
		}
		if method != http.MethodDelete || path != "/api/v1/workspaces/ws/agents/ag/runtime/schedules/sch_1" {
			t.Errorf("req: %s %s", method, path)
		}
	})
	t.Run("PathEscape on slash/space ID", func(t *testing.T) {
		// url.PathEscape escapes the ID; r.URL.Path is decoded by Go's server,
		// so we verify via EscapedPath() that the raw request contained %2F/%20.
		var escaped string
		srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			escaped = r.URL.EscapedPath()
			w.WriteHeader(http.StatusNoContent)
		}))
		defer srv2.Close()
		api2 := scheduledMessageRemote{client: &apiclient.Client{ServerURL: srv2.URL, AgentID: "ag", AuthToken: "tok", RouteWorkspaceID: "ws", HTTPClient: srv2.Client()}}
		if err := api2.delete(context.Background(), "sch/1 sp"); err != nil {
			t.Fatal(err)
		}
		if escaped != "/api/v1/workspaces/ws/agents/ag/runtime/schedules/sch%2F1%20sp" {
			t.Errorf("escaped path: %s", escaped)
		}
	})
	t.Run("404 maps to not_found via classifyAPIError", func(t *testing.T) {
		notFound := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusNotFound)
		}))
		defer notFound.Close()
		bad := scheduledMessageRemote{client: &apiclient.Client{ServerURL: notFound.URL, AgentID: "ag", AuthToken: "tok", RouteWorkspaceID: "ws", HTTPClient: notFound.Client()}}
		_, err := bad.create(context.Background(), scheduledMessageCreateRequest{scheduledMessageWriteRequest: validScheduledMessageWriteRequest("N")})
		classified := classifyAPIError(err, "test")
		var e *Error
		if !errors.As(classified, &e) || e.Code != CodeNotFound {
			t.Fatalf("expected not_found, got: %v", err)
		}
	})
}

func validScheduledMessageWriteRequest(name string) scheduledMessageWriteRequest {
	return scheduledMessageWriteRequest{
		Name:     name,
		Schedule: scheduledMessageSchedule{Kind: "every", Seconds: 60, Anchor: "2026-07-21T00:00:00Z"},
		Target:   "#ops",
		Template: "hello",
	}
}
