package exec

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

	"github.com/stretchr/testify/require"

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

func TestApiclientScheduledMessageAPI(t *testing.T) {
	var method, path, escapedPath, rawQuery, auth string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		method, path, escapedPath, rawQuery, auth = r.Method, r.URL.Path, r.URL.EscapedPath(), r.URL.RawQuery, r.Header.Get("Authorization")
		switch {
		case strings.Contains(r.URL.Path, "/runs"):
			_, _ = io.WriteString(w, `[{"id":"run_1"}]`) // top-level array
		case path == "/api/v1/workspaces/ws/agents/ag/runtime/schedules" || strings.HasSuffix(path, "/schedules"):
			_, _ = io.WriteString(w, `{"schedules":[{"id":"sch_1","name":"N","status":"active","target":{"kind":"channel","ref":"#ops","label":"Ops"}}],"limit":5,"cursor":"c1","hasMore":true,"nextCursor":"c2"}`)
		case strings.Contains(r.URL.Path, "/schedules/"):
			_, _ = io.WriteString(w, `{"id":"sch_2","name":"M","status":"paused"}`)
		}
	}))
	defer srv.Close()
	api := scheduledMessageRemote{client: &apiclient.Client{ServerURL: srv.URL, AgentID: "ag", AuthToken: "tok", RouteWorkspaceID: "ws", HTTPClient: srv.Client()}}

	t.Run("list GET with bearer and full DTO", func(t *testing.T) {
		method, path, auth = "", "", ""
		res, err := api.list(context.Background(), scheduledMessageListRequest{Limit: 5})
		if err != nil {
			t.Fatal(err)
		}
		if method != http.MethodGet {
			t.Errorf("method: %s", method)
		}
		if auth != "Bearer tok" {
			t.Errorf("bearer: %q", auth)
		}
		if path != "/api/v1/workspaces/ws/agents/ag/runtime/schedules" || rawQuery != "limit=5&page=1" {
			t.Errorf("list URL: %s?%s", path, rawQuery)
		}
		if len(res.Items) != 1 || res.Items[0].ID != "sch_1" || res.Items[0].Target.Ref != "#ops" {
			t.Errorf("items: %+v", res.Items)
		}
		if !res.HasMore || res.NextCursor != "c2" || res.Limit != 5 {
			t.Errorf("page: %+v", res)
		}
	})
	t.Run("list cursor in query string", func(t *testing.T) {
		var gotPath, gotQuery string
		srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			gotPath = r.URL.EscapedPath()
			gotQuery = r.URL.RawQuery
			_, _ = io.WriteString(w, `{"schedules":[]}`)
		}))
		defer srv2.Close()
		api2 := scheduledMessageRemote{client: &apiclient.Client{ServerURL: srv2.URL, AgentID: "ag", AuthToken: "tok", RouteWorkspaceID: "ws", HTTPClient: srv2.Client()}}
		_, err := api2.list(context.Background(), scheduledMessageListRequest{Limit: 10, Cursor: "abc123"})
		if err != nil {
			t.Fatal(err)
		}
		if gotPath != "/api/v1/workspaces/ws/agents/ag/runtime/schedules" || gotQuery != "cursor=abc123&limit=10&page=1" {
			t.Errorf("list URL: %s?%s", gotPath, gotQuery)
		}
	})
	t.Run("get GET by id returns raw", func(t *testing.T) {
		method, path = "", ""
		raw, err := api.get(context.Background(), "sch_2")
		if err != nil {
			t.Fatal(err)
		}
		if !strings.Contains(path, "/schedules/sch_2") {
			t.Errorf("path: %s", path)
		}
		if raw.ID != "sch_2" {
			t.Errorf("result: %+v", raw)
		}
	})
	t.Run("runs top-level array", func(t *testing.T) {
		runs, err := api.runs(context.Background(), "sch_1", 10)
		if err != nil {
			t.Fatal(err)
		}
		if len(runs) != 1 || runs[0].ID != "run_1" {
			t.Errorf("runs: %+v", runs)
		}
	})
	t.Run("get and runs escape slash and space IDs", func(t *testing.T) {
		id := "sch/1 space"
		if _, err := api.get(context.Background(), id); err != nil {
			t.Fatal(err)
		}
		if escapedPath != "/api/v1/workspaces/ws/agents/ag/runtime/schedules/sch%2F1%20space" {
			t.Errorf("get escaped path: %s", escapedPath)
		}
		if _, err := api.runs(context.Background(), id, 7); err != nil {
			t.Fatal(err)
		}
		if escapedPath != "/api/v1/workspaces/ws/agents/ag/runtime/schedules/sch%2F1%20space/runs" || rawQuery != "limit=7" {
			t.Errorf("runs URL: %s?%s", escapedPath, rawQuery)
		}
	})
	t.Run("missing credentials stable error", func(t *testing.T) {
		empty := scheduledMessageRemote{client: &apiclient.Client{ServerURL: srv.URL, RouteWorkspaceID: "ws"}}
		if _, err := empty.list(context.Background(), scheduledMessageListRequest{Limit: 20}); err == nil {
			t.Fatal("expected credential error")
		}
	})
}

func TestScheduledMessageTransportShapes(t *testing.T) {
	tests := []struct {
		name   string
		reason string
		raw    string
		decode func([]byte) error
	}{
		{
			name:   "runtime mutation record projection",
			reason: "the runtime returns schedule fields at the top level",
			raw:    `{"id":"sch_2","name":"legacy"}`,
			decode: func(raw []byte) error {
				var result scheduledMessageRecordTransport
				if err := json.Unmarshal(raw, &result); err != nil {
					return err
				}
				require.Equal(t, "sch_2", result.model().ID)
				return nil
			},
		},
		{
			name:   "canonical runs envelope",
			reason: "current runtime run pages wrap runs",
			raw:    `{"runs":[{"id":"run_1"}]}`,
			decode: func(raw []byte) error {
				var result scheduledMessageRunsTransportResponse
				if err := json.Unmarshal(raw, &result); err != nil {
					return err
				}
				require.Len(t, result.Runs, 1)
				return nil
			},
		},
		{
			name:   "server runs array",
			reason: "the runtime returns runs as a top-level array",
			raw:    `[{"id":"run_2"}]`,
			decode: func(raw []byte) error {
				var result scheduledMessageRunsTransportResponse
				if err := json.Unmarshal(raw, &result); err != nil {
					return err
				}
				require.Len(t, result.Runs, 1)
				return nil
			},
		},
		{
			name:   "paged list envelope",
			reason: "the runtime wraps cursor pages in schedules",
			raw:    `{"schedules":[{"id":"sch_3"}]}`,
			decode: func(raw []byte) error {
				var result scheduledMessageListTransportResponse
				if err := json.Unmarshal(raw, &result); err != nil {
					return err
				}
				require.Len(t, result.Schedules, 1)
				return nil
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			require.NotEmpty(t, tt.reason)
			require.NoError(t, tt.decode([]byte(tt.raw)))
		})
	}
}
