package httpapi

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

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

func TestKlingV1O1CompatibilityBody(t *testing.T) {
	body, externalID, err := klingCompatTaskBody("v1", klingO1Model, map[string]any{
		"prompt":           "一只纸鹤飞过湖面",
		"duration":         json.Number("10"),
		"aspect_ratio":     "16:9",
		"sound":            "on",
		"external_task_id": "client-o1-1",
		"watermark_info":   map[string]any{"enabled": true},
	})
	if err != nil {
		t.Fatalf("build O1 compatibility body: %v", err)
	}
	if externalID != "client-o1-1" || body["model"] != klingO1Model || body["modelType"] != "omni_video" {
		t.Fatalf("unexpected identity fields: %#v", body)
	}
	if body["mode"] != "pro" || body["resolution"] != "1080p" || body["audio"] != true {
		t.Fatalf("unexpected V1 defaults: %#v", body)
	}
	content := mapListFromRequest(body["content"])
	if len(content) != 1 || content[0]["type"] != "text" || content[0]["text"] != "一只纸鹤飞过湖面" {
		t.Fatalf("unexpected canonical content: %#v", content)
	}
}

func TestKlingV1V3OmniCompatibilityBody(t *testing.T) {
	body, _, err := klingCompatTaskBody("v1", klingV3OmniModel, map[string]any{
		"multi_shot": true,
		"shot_type":  "customize",
		"mode":       "4k",
		"multi_prompt": []any{
			map[string]any{"index": json.Number("1"), "prompt": "推近人物", "duration": json.Number("7")},
			map[string]any{"index": json.Number("2"), "prompt": "切到城市远景", "duration": json.Number("8")},
		},
		"image_list": []any{
			map[string]any{"image_url": "https://example.com/first.png", "type": "first_frame"},
		},
	})
	if err != nil {
		t.Fatalf("build 3.0 Omni compatibility body: %v", err)
	}
	if body["resolution"] != "2160p" {
		t.Fatalf("4k mode was not normalized: %#v", body)
	}
	content := mapListFromRequest(body["content"])
	if len(content) != 1 || content[0]["role"] != "first_frame" {
		t.Fatalf("image input was not normalized: %#v", content)
	}
}

func TestKlingV2CompatibilityBody(t *testing.T) {
	body, externalID, err := klingCompatTaskBody("v2", klingV3OmniModel, map[string]any{
		"contents": []any{
			map[string]any{"type": "prompt", "text": "让角色向镜头挥手"},
			map[string]any{"type": "first_frame", "url": "https://example.com/first.png"},
			map[string]any{"type": "element", "id": json.Number("42")},
		},
		"settings": map[string]any{
			"resolution":   "1080p",
			"duration":     json.Number("15"),
			"aspect_ratio": "9:16",
			"audio":        "native",
		},
		"options": map[string]any{
			"external_task_id": "client-v2-1",
			"callback_url":     "https://example.com/callback",
		},
	})
	if err != nil {
		t.Fatalf("build V2 compatibility body: %v", err)
	}
	if externalID != "client-v2-1" || body["mode"] != "pro" || body["audio"] != true {
		t.Fatalf("unexpected V2 settings: %#v", body)
	}
	if len(mapListFromRequest(body["image_list"])) != 1 || len(mapListFromRequest(body["element_list"])) != 1 {
		t.Fatalf("unexpected V2 references: %#v", body)
	}
}

func TestKlingSubmissionEnvelopesDoNotReplayCurrentResult(t *testing.T) {
	task := store.GatewayTask{
		ID: "gateway-task", Status: "succeeded", RemoteTaskID: "upstream-task",
		CompatibilityProtocol:       "kling-v1-omni",
		CompatibilitySourceProtocol: "kling-v1-omni",
		Result:                      map[string]any{"data": []any{map[string]any{"url": "https://example.invalid/result.mp4"}}},
		CreatedAt:                   time.Unix(100, 0),
		UpdatedAt:                   time.Unix(101, 0),
	}
	v1 := klingV1SubmissionEnvelope(task)
	v1Data, _ := v1["data"].(map[string]any)
	if v1Data["task_status"] != "submitted" || v1Data["task_result"] != nil {
		t.Fatalf("V1 submission envelope=%#v", v1)
	}
	v2 := klingV2SubmissionEnvelope(task)
	v2Data, _ := v2["data"].(map[string]any)
	if v2Data["status"] != "submitted" || v2Data["outputs"] != nil {
		t.Fatalf("V2 submission envelope=%#v", v2)
	}
}

func TestKlingCompatibilityValidation(t *testing.T) {
	tests := []struct {
		name  string
		model string
		body  map[string]any
	}{
		{name: "O1 duration", model: klingO1Model, body: map[string]any{"prompt": "test", "duration": 11}},
		{name: "O1 text-only flexible duration", model: klingO1Model, body: map[string]any{"prompt": "test", "duration": 3}},
		{name: "O1 4k", model: klingO1Model, body: map[string]any{"prompt": "test", "duration": 5, "mode": "4k"}},
		{name: "O1 multi-shot", model: klingO1Model, body: map[string]any{"multi_shot": true, "multi_prompt": []any{map[string]any{"prompt": "test", "duration": 3}}}},
		{name: "custom multi-shot without prompts", model: klingV3OmniModel, body: map[string]any{"multi_shot": true, "shot_type": "customize"}},
		{name: "intelligence multi-shot without prompt", model: klingV3OmniModel, body: map[string]any{"multi_shot": true, "shot_type": "intelligence"}},
		{name: "video with native audio", model: klingV3OmniModel, body: map[string]any{"prompt": "test", "sound": "on", "video_list": []any{map[string]any{"video_url": "https://example.com/input.mp4"}}}},
		{name: "video duration too long", model: klingV3OmniModel, body: map[string]any{"prompt": "test", "duration": 15, "video_list": []any{map[string]any{"video_url": "https://example.com/input.mp4"}}}},
		{name: "too many references", model: klingV3OmniModel, body: map[string]any{"prompt": "test", "image_list": []any{
			map[string]any{}, map[string]any{}, map[string]any{}, map[string]any{}, map[string]any{}, map[string]any{}, map[string]any{}, map[string]any{},
		}}},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			if _, _, err := klingCompatTaskBody("v1", test.model, test.body); err == nil {
				t.Fatal("expected validation error")
			}
		})
	}
	if _, _, err := klingCompatTaskBody("v1", klingV3OmniModel, map[string]any{"prompt": "test", "duration": 15}); err != nil {
		t.Fatalf("3.0 Omni should allow a 15-second duration: %v", err)
	}
}

func TestDecodeKlingJSONRejectsTrailingData(t *testing.T) {
	request := httptest.NewRequest(http.MethodPost, "/kling/v1/videos/omni-video", strings.NewReader(`{"prompt":"ok"} trailing`))
	var body map[string]any
	if err := decodeKlingJSON(request, &body); err == nil {
		t.Fatal("expected trailing JSON error")
	}
}

func TestWriteKlingCompatErrorUsesOfficialNumericEnvelope(t *testing.T) {
	recorder := httptest.NewRecorder()
	writeKlingCompatError(recorder, http.StatusBadRequest, "bad request", "invalid_parameter")
	var body map[string]any
	if err := json.Unmarshal(recorder.Body.Bytes(), &body); err != nil {
		t.Fatalf("decode error response: %v", err)
	}
	if body["code"] != float64(1001) || body["error"] != "invalid_parameter" {
		t.Fatalf("unexpected error envelope: %#v", body)
	}
}
