package runner

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

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)

func TestMinimalRemoteTaskCheckpointUsesAllowlist(t *testing.T) {
	checkpoint := minimalRemoteTaskCheckpoint("keling", "", map[string]any{
		"endpoint":          "/videos/task",
		"mode":              "omni_video",
		"cleanupElementIds": []any{"element-1"},
		"phase":             "uploaded",
		"submit":            map[string]any{"large": "provider response"},
		"payload":           map[string]any{"prompt": "duplicate request"},
		"raw":               map[string]any{"data": "duplicate result"},
		"status":            "processing",
	})
	if checkpoint["endpoint"] != "/videos/task" || checkpoint["mode"] != "omni_video" || checkpoint["cleanupElementIds"] == nil {
		t.Fatalf("checkpoint lost required fields: %#v", checkpoint)
	}
	for _, forbidden := range []string{"phase", "submit", "payload", "raw", "status"} {
		if _, ok := checkpoint[forbidden]; ok {
			t.Fatalf("checkpoint contains forbidden field %q: %#v", forbidden, checkpoint)
		}
	}
	if unknown := minimalRemoteTaskCheckpoint("unknown-provider", "", map[string]any{"endpoint": "/forbidden"}); len(unknown) != 0 {
		t.Fatalf("unknown provider checkpoint=%#v, want empty", unknown)
	}
}

func TestCanonicalTaskResultDropsProviderRawCopy(t *testing.T) {
	result := canonicalTaskResult(map[string]any{
		"id":                 "task",
		"data":               []any{map[string]any{"url": "https://example.com/output"}},
		"raw":                map[string]any{"provider": "duplicate"},
		"raw_data":           map[string]any{"provider": "duplicate"},
		"provider_response":  map[string]any{"provider": "duplicate"},
		"upstream_task_id":   "remote-task",
		"compatibility_data": "kept",
	})
	if result["raw"] != nil || result["raw_data"] != nil || result["provider_response"] != nil ||
		result["upstream_task_id"] != nil || result["id"] != "task" || result["data"] == nil ||
		result["compatibility_data"] != "kept" {
		t.Fatalf("canonical result=%#v", result)
	}
}

func TestMinimalParameterPreprocessingChangesDropsValuesAndCapsSize(t *testing.T) {
	changes := make([]parameterPreprocessChange, 0, 100)
	for index := 0; index < 100; index++ {
		changes = append(changes, parameterPreprocessChange{
			Path:   "input.image",
			Action: "converted",
			Before: map[string]any{"base64": "forbidden"},
			After:  map[string]any{"url": "forbidden"},
		})
	}
	minimal := minimalParameterPreprocessingChanges(changes)
	encoded, err := json.Marshal(minimal)
	if err != nil {
		t.Fatal(err)
	}
	if len(encoded) > 1024 {
		t.Fatalf("minimal changes size=%d, want <=1024", len(encoded))
	}
	for _, raw := range minimal {
		item := raw.(map[string]any)
		if len(item) != 2 || item["path"] != "input.image" || item["action"] != "converted" {
			t.Fatalf("unexpected minimal change: %#v", item)
		}
	}
}

func TestDeliverTaskCallbackSendsMinimalAuthenticatedBody(t *testing.T) {
	createdAt := time.Date(2026, time.July, 24, 10, 0, 0, 0, time.UTC)
	var received map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
		if request.Header.Get("Authorization") != "Bearer internal-token" {
			t.Errorf("Authorization=%q", request.Header.Get("Authorization"))
		}
		if request.Header.Get("Idempotency-Key") != "task-id:3" {
			t.Errorf("Idempotency-Key=%q", request.Header.Get("Idempotency-Key"))
		}
		if request.Header.Get("X-EasyAI-Event-Type") != "task.completed" {
			t.Errorf("X-EasyAI-Event-Type=%q", request.Header.Get("X-EasyAI-Event-Type"))
		}
		if err := json.NewDecoder(request.Body).Decode(&received); err != nil {
			t.Error(err)
		}
		w.WriteHeader(http.StatusNoContent)
	}))
	defer server.Close()

	status, err := deliverTaskCallback(context.Background(), server.Client(), store.TaskCallbackDelivery{
		TaskID:      "task-id",
		Seq:         3,
		CallbackURL: server.URL,
		EventType:   "task.completed",
		TaskStatus:  "succeeded",
		CreatedAt:   createdAt,
	}, "internal-token")
	if err != nil || status != http.StatusNoContent {
		t.Fatalf("status=%d err=%v", status, err)
	}
	if len(received) != 5 {
		t.Fatalf("callback fields=%#v, want exactly five fields", received)
	}
	if received["taskId"] != "task-id" || received["eventType"] != "task.completed" || received["status"] != "succeeded" {
		t.Fatalf("callback body=%#v", received)
	}
}

func TestDeliverFailedTaskCallbackUsesStandardErrorWithoutSocketDetails(t *testing.T) {
	var received map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
		if err := json.NewDecoder(request.Body).Decode(&received); err != nil {
			t.Error(err)
		}
		w.WriteHeader(http.StatusNoContent)
	}))
	defer server.Close()

	raw := "read tcp 10.42.0.72:54960->47.77.191.126:443: read: connection reset by peer"
	status, err := deliverTaskCallback(t.Context(), server.Client(), store.TaskCallbackDelivery{
		TaskID:           "failed-task",
		Seq:              4,
		CallbackURL:      server.URL,
		EventType:        "task.failed",
		TaskStatus:       "failed",
		TaskErrorCode:    "response_read_error",
		TaskErrorMessage: raw,
		CreatedAt:        time.Now(),
	}, "")
	if err != nil || status != http.StatusNoContent {
		t.Fatalf("status=%d err=%v", status, err)
	}
	errorValue, _ := received["error"].(map[string]any)
	if errorValue["code"] != "upstream_connection_interrupted" || strings.Contains(stringFromAny(errorValue["message"]), "10.42.0.72") {
		t.Fatalf("unsafe callback error: %#v", errorValue)
	}
}
