package exec

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

	"github.com/stretchr/testify/require"

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

func TestScheduledMessageDomainKeepsNineActions(t *testing.T) {
	catalog, err := DefaultCatalog()
	require.NoError(t, err)
	count := 0
	for _, action := range catalog.actionsSnapshot() {
		if action.Domain == "scheduled-message" {
			count++
		}
	}
	require.Equal(t, 9, count)
}

func TestScheduledMessageP0InputSchemas(t *testing.T) {
	catalog, err := DefaultCatalog()
	require.NoError(t, err)

	validWrites := []string{
		`{"name":"release","schedule":{"kind":"at","at":"2026-07-30T09:00:00Z"},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"after","seconds":60},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"every","seconds":60,"anchor":"2026-07-30T09:00:00Z"},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"cron","expression":"0 9 * * 1","timezone":"UTC"},"target":"#ops","template":"ship"}`,
	}
	for index, raw := range validWrites {
		var create map[string]any
		require.NoError(t, json.Unmarshal([]byte(raw), &create))
		create["idempotencyKey"] = "create-" + string(rune('1'+index))
		createEncoded, err := json.Marshal(create)
		require.NoError(t, err)
		require.NoError(t, catalog.ValidateInput("scheduled-message", "create", createEncoded), string(createEncoded))
		require.NoError(t, catalog.ValidateInput("scheduled-message", "validate", json.RawMessage(raw)), raw)
		var update map[string]any
		require.NoError(t, json.Unmarshal([]byte(raw), &update))
		update["id"] = "sch_1"
		update["idempotencyKey"] = "update-" + string(rune('1'+index))
		encoded, err := json.Marshal(update)
		require.NoError(t, err)
		require.NoError(t, catalog.ValidateInput("scheduled-message", "update", encoded), string(encoded))
	}

	invalid := []string{
		`{"name":"release","schedule":{"kind":"after"},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"after","seconds":0},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"after","seconds":60,"timezone":"UTC"},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"every","seconds":60},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"cron","expression":"0 9 * * 1"},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"interval","seconds":60},"target":"#ops","template":"ship"}`,
		`{"name":"","schedule":{"kind":"after","seconds":60},"target":"#ops","template":"ship"}`,
		`{"name":"release","schedule":{"kind":"after","seconds":60},"target":"#ops","template":""}`,
	}
	for _, raw := range invalid {
		var create map[string]any
		require.NoError(t, json.Unmarshal([]byte(raw), &create))
		create["idempotencyKey"] = "create-invalid"
		encoded, err := json.Marshal(create)
		require.NoError(t, err)
		require.Error(t, catalog.ValidateInput("scheduled-message", "create", encoded), raw)
	}
	require.Error(t, catalog.ValidateInput("scheduled-message", "create", json.RawMessage(`{"name":"release","schedule":{"kind":"after","seconds":60},"target":"#ops","template":"ship"}`)))
	require.Error(t, catalog.ValidateInput("scheduled-message", "create", json.RawMessage(`{"name":"release","schedule":{"kind":"after","seconds":60},"target":"#ops","template":"ship","idempotencyKey":"`+strings.Repeat("x", 129)+`"}`)))

	for _, action := range []string{"get", "pause", "resume", "delete"} {
		require.NoError(t, catalog.ValidateInput("scheduled-message", action, json.RawMessage(`{"id":"sch_1"}`)))
		require.Error(t, catalog.ValidateInput("scheduled-message", action, json.RawMessage(`{"scheduledMessageId":"sch_1"}`)))
	}
	require.NoError(t, catalog.ValidateInput("scheduled-message", "runs", json.RawMessage(`{"id":"sch_1","limit":100}`)))
	require.Error(t, catalog.ValidateInput("scheduled-message", "runs", json.RawMessage(`{"id":"sch_1","limit":101}`)))
	require.NoError(t, catalog.ValidateInput("scheduled-message", "list", json.RawMessage(`{"limit":100,"cursor":"3"}`)))
	require.NoError(t, catalog.ValidateInput("scheduled-message", "list", json.RawMessage(`{"status":"active","source":"time","target":"#ops"}`)))
	require.Error(t, catalog.ValidateInput("scheduled-message", "list", json.RawMessage(`{"limit":101}`)))
}

func TestScheduledMessageP0TransportAndProjection(t *testing.T) {
	var requests []struct {
		method string
		path   string
		query  url.Values
		body   map[string]any
	}
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		entry := struct {
			method string
			path   string
			query  url.Values
			body   map[string]any
		}{method: r.Method, path: r.URL.Path, query: r.URL.Query()}
		if r.Body != nil {
			raw, _ := io.ReadAll(r.Body)
			if len(strings.TrimSpace(string(raw))) > 0 {
				require.NoError(t, json.Unmarshal(raw, &entry.body))
			}
		}
		requests = append(requests, entry)

		switch {
		case strings.HasSuffix(r.URL.Path, "/runs"):
			_, _ = io.WriteString(w, `[{"id":"run_1"}]`)
		case strings.HasSuffix(r.URL.Path, "/validate"):
			_, _ = io.WriteString(w, `{"proposed":{"id":"sch_1","name":"release","status":"active","sourceKind":"time","timeMode":"cron","cronExpression":"0 9 * * 1","timezone":"UTC","target":{"kind":"channel","ref":"chn_1"},"messageTemplate":"ship","alsoStartThread":true,"humanNotificationMode":"none","createdAt":"2026-07-29T00:00:00Z","updatedAt":"2026-07-29T00:00:00Z","validation":{"valid":true}}}`)
		default:
			_, _ = io.WriteString(w, `{"id":"sch_1","name":"release","status":"active","sourceKind":"time","timeMode":"cron","cronExpression":"0 9 * * 1","timezone":"UTC","target":{"kind":"channel","ref":"chn_1"},"messageTemplate":"ship","alsoStartThread":true,"humanNotificationMode":"none","createdAt":"2026-07-29T00:00:00Z","updatedAt":"2026-07-29T00:00:00Z","validation":{"valid":true}}`)
		}
	}))
	t.Cleanup(server.Close)

	remote := scheduledMessageRemote{client: &apiclient.Client{
		ServerURL: server.URL, RouteWorkspaceID: "ws", AgentID: "ag", AuthToken: "token", HTTPClient: server.Client(),
	}}
	write := scheduledMessageWriteRequest{
		Name: "release", Schedule: scheduledMessageSchedule{Kind: "cron", Expression: "0 9 * * 1", Timezone: "UTC"},
		Target: "#ops", Template: "ship", StartThread: true, HumanNotificationMode: "none",
	}
	created, err := remote.create(context.Background(), scheduledMessageCreateRequest{scheduledMessageWriteRequest: write, IdempotencyKey: "idem_1"})
	require.NoError(t, err)
	require.Equal(t, "ship", created.Template)
	require.True(t, created.StartThread)
	require.Equal(t, scheduledMessageSchedule{Kind: "cron", Expression: "0 9 * * 1", Timezone: "UTC"}, created.Schedule)
	require.True(t, created.Validation.Valid)

	_, err = remote.validate(context.Background(), scheduledMessageValidateRequest{ID: "sch_1", scheduledMessageWriteRequest: write})
	require.NoError(t, err)
	_, err = remote.update(context.Background(), "sch_1", scheduledMessageUpdateRequest{ID: "sch_1", scheduledMessageWriteRequest: write, ExpectedUpdatedAt: "2026-07-29T00:00:00Z"})
	require.NoError(t, err)

	require.Len(t, requests, 3)
	for _, request := range requests {
		require.Equal(t, map[string]any{
			"kind": "cron", "expression": "0 9 * * 1", "timezone": "UTC",
		}, request.body["schedule"])
		require.Equal(t, "ship", request.body["template"])
		require.Equal(t, true, request.body["startThread"])
		require.NotContains(t, request.body, "source")
		require.NotContains(t, request.body, "messageTemplate")
		require.NotContains(t, request.body, "alsoStartThread")
	}
	require.Equal(t, "sch_1", requests[1].body["scheduleId"])

	handler := scheduledMessageHandler{}
	runs, err := handler.runs(context.Background(), actionScopedJSON[scheduledMessageRunsRequest]{
		Authorized: runtimeAuthorization{Server: remote.client}, Body: scheduledMessageRunsRequest{ID: "sch_1", Limit: 20},
	})
	require.NoError(t, err)
	encoded, err := json.Marshal(runs.Data)
	require.NoError(t, err)
	require.JSONEq(t, `{"runs":[{"id":"run_1","workspaceId":"","idempotencyKey":"","sourceOccurrenceRef":"","sourceKind":"","status":"","attemptedAt":"","renderedContentPreview":"","attemptCount":0,"target":{}}],"hasMore":false}`, string(encoded))
}

func TestScheduledMessageListForwardsOnlySupportedFilters(t *testing.T) {
	var query url.Values
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		query = r.URL.Query()
		_, _ = io.WriteString(w, `{"schedules":[],"limit":10,"hasMore":false}`)
	}))
	t.Cleanup(server.Close)

	remote := scheduledMessageRemote{client: &apiclient.Client{
		ServerURL: server.URL, RouteWorkspaceID: "ws", AgentID: "ag", AuthToken: "token", HTTPClient: server.Client(),
	}}
	_, err := remote.list(context.Background(), scheduledMessageListRequest{
		Limit: 10, Cursor: "3", Status: "active", Source: "time", Target: "#ops",
	})
	require.NoError(t, err)
	require.Equal(t, "10", query.Get("limit"))
	require.Equal(t, "3", query.Get("cursor"))
	require.Equal(t, "1", query.Get("page"))
	require.Equal(t, "active", query.Get("status"))
	require.Equal(t, "time", query.Get("source"))
	require.Equal(t, "#ops", query.Get("target"))
}
