package httpapi

import (
	"context"
	"io"
	"log/slog"
	"net/http"
	"net/http/httptest"
	"os"
	"strconv"
	"strings"
	"testing"
	"time"

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

func TestKeling30TurboSimulationFlow(t *testing.T) {
	databaseURL := strings.TrimSpace(os.Getenv("AI_GATEWAY_TEST_DATABASE_URL"))
	if databaseURL == "" {
		t.Skip("set AI_GATEWAY_TEST_DATABASE_URL to run the Kling simulation integration flow")
	}

	ctx := context.Background()
	applyMigration(t, ctx, databaseURL)
	db, err := store.Connect(ctx, databaseURL)
	if err != nil {
		t.Fatalf("connect store: %v", err)
	}
	defer db.Close()

	serverCtx, cancelServer := context.WithCancel(ctx)
	defer cancelServer()
	server := httptest.NewServer(NewServerWithContext(serverCtx, config.Config{
		AppEnv:                            "test",
		HTTPAddr:                          ":0",
		DatabaseURL:                       databaseURL,
		IdentityMode:                      "hybrid",
		JWTSecret:                         "test-secret",
		CORSAllowedOrigin:                 "*",
		AsyncQueueWorkerEnabled:           true,
		AsyncWorkerHardLimit:              32,
		AsyncWorkerInstanceHardLimit:      32,
		AsyncWorkerRefreshIntervalSeconds: 1,
	}, db, slog.New(slog.NewTextHandler(io.Discard, nil))))
	defer server.Close()

	suffix := strconv.FormatInt(time.Now().UnixNano(), 10)
	username := "kling_sim_" + suffix
	password := "password123"
	var registerResponse struct {
		AccessToken string `json:"accessToken"`
	}
	doJSON(t, server.URL, http.MethodPost, "/api/v1/auth/register", "", map[string]any{
		"username": username,
		"email":    username + "@example.com",
		"password": password,
	}, http.StatusCreated, &registerResponse)

	var apiKeyResponse struct {
		Secret string `json:"secret"`
	}
	doJSON(t, server.URL, http.MethodPost, "/api/v1/api-keys", registerResponse.AccessToken, map[string]any{
		"name": "kling simulation key",
	}, http.StatusCreated, &apiKeyResponse)

	if _, err := db.Pool().Exec(ctx, `
UPDATE gateway_users
SET roles = '["admin"]'::jsonb
WHERE username = $1`, username); err != nil {
		t.Fatalf("promote Kling simulation user: %v", err)
	}
	var loginResponse struct {
		AccessToken string `json:"accessToken"`
	}
	doJSON(t, server.URL, http.MethodPost, "/api/v1/auth/login", "", map[string]any{
		"account":  username,
		"password": password,
	}, http.StatusOK, &loginResponse)

	var gatewayUserID string
	if err := db.Pool().QueryRow(
		ctx,
		`SELECT id::text FROM gateway_users WHERE username = $1`,
		username,
	).Scan(&gatewayUserID); err != nil {
		t.Fatalf("read Kling simulation user id: %v", err)
	}
	doJSON(t, server.URL, http.MethodPatch, "/api/admin/users/"+gatewayUserID+"/wallet", loginResponse.AccessToken, map[string]any{
		"currency": "resource",
		"balance":  1000,
		"reason":   "seed Kling simulation wallet",
	}, http.StatusOK, nil)

	var platform struct {
		ID string `json:"id"`
	}
	doJSON(t, server.URL, http.MethodPost, "/api/admin/platforms", loginResponse.AccessToken, map[string]any{
		"provider":    "keling",
		"platformKey": "keling-simulation-" + suffix,
		"name":        "Kling Simulation",
		"baseUrl":     "https://api-beijing.klingai.com/v1",
		"authType":    "AccessKey-SecretKey",
		"credentials": map[string]any{
			"accessKey": "legacy-ak",
			"secretKey": "legacy-sk",
		},
	}, http.StatusCreated, &platform)

	doJSON(t, server.URL, http.MethodPost, "/api/admin/platforms/"+platform.ID+"/models", loginResponse.AccessToken, map[string]any{
		"canonicalModelKey": "keling:kling-3.0-turbo",
		"modelName":         "kling-3.0-turbo",
		"providerModelName": "kling-3.0-turbo",
		"modelAlias":        "可灵3.0 Turbo",
		"modelType":         []string{"video_generate", "image_to_video"},
		"displayName":       "可灵3.0 Turbo",
	}, http.StatusCreated, nil)

	assertKeling30TurboSimulationTask := func(
		name string,
		request map[string]any,
		expectedModelType string,
	) {
		t.Helper()
		t.Run(name, func(t *testing.T) {
			var response struct {
				Task struct {
					ID                 string         `json:"id"`
					Status             string         `json:"status"`
					RunMode            string         `json:"runMode"`
					ModelType          string         `json:"modelType"`
					ResolvedModel      string         `json:"resolvedModel"`
					Result             map[string]any `json:"result"`
					Metrics            map[string]any `json:"metrics"`
					BillingSummary     map[string]any `json:"billingSummary"`
					FinalChargeAmount  float64        `json:"finalChargeAmount"`
					ResponseDurationMS int64          `json:"responseDurationMs"`
				} `json:"task"`
			}
			doJSONWithHeaders(
				t,
				server.URL,
				http.MethodPost,
				"/api/v1/videos/generations",
				apiKeyResponse.Secret,
				request,
				map[string]string{"X-Async": "true"},
				http.StatusAccepted,
				&response,
			)
			if response.Task.ID == "" {
				t.Fatal("async Kling simulation response did not return a task id")
			}
			waitForTaskStatus(t, server.URL, apiKeyResponse.Secret, response.Task.ID, []string{"succeeded"}, 10*time.Second)
			doJSON(t, server.URL, http.MethodGet, "/api/v1/tasks/"+response.Task.ID, apiKeyResponse.Secret, nil, http.StatusOK, &response.Task)

			task := response.Task
			if task.ID == "" ||
				task.Status != "succeeded" ||
				task.RunMode != "simulation" ||
				task.ModelType != expectedModelType ||
				task.ResolvedModel != "kling-3.0-turbo" {
				t.Fatalf("unexpected Kling simulation task: %+v", task)
			}
			data, _ := task.Result["data"].([]any)
			item, _ := data[0].(map[string]any)
			if item["video_url"] != "/static/simulation/video.mp4" ||
				item["assetSource"] != "simulation" {
				t.Fatalf("unexpected Kling simulation result: %+v", task.Result)
			}
			if task.FinalChargeAmount <= 0 ||
				task.BillingSummary["finalCharge"] == nil ||
				task.Metrics["parameterPreprocessingSummary"] == nil ||
				task.ResponseDurationMS <= 0 {
				t.Fatalf("Kling simulation should preserve billing, preprocessing and timing: %+v", task)
			}
		})
	}

	assertKeling30TurboSimulationTask("text-to-video", map[string]any{
		"model":                "可灵3.0 Turbo",
		"runMode":              "simulation",
		"simulation":           true,
		"simulationDurationMs": 5,
		"prompt":               "A cinematic city reveal",
		"duration":             8,
		"resolution":           "1080p",
		"aspect_ratio":         "9:16",
		"audio":                true,
	}, "video_generate")

	assertKeling30TurboSimulationTask("image-to-video", map[string]any{
		"model":                "可灵3.0 Turbo",
		"runMode":              "simulation",
		"simulation":           true,
		"simulationDurationMs": 5,
		"prompt":               "The subject looks toward the camera",
		"image":                server.URL + "/static/simulation/image.png",
		"duration":             5,
		"resolution":           "720p",
		"audio":                true,
	}, "image_to_video")
}
