package integration

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

	"nex/backend/internal/conversion"
	"nex/backend/internal/conversion/anthropic"
	"nex/backend/internal/handler"
	"nex/backend/internal/handler/middleware"
	"nex/backend/internal/provider"
	"nex/backend/internal/repository"
	"nex/backend/internal/service"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gorm.io/gorm"

	openaiConv "nex/backend/internal/conversion/openai"
)

func init() {
	gin.SetMode(gin.TestMode)
}

// setupConversionTest 创建包含 ConversionEngine 的完整测试环境
func setupConversionTest(t *testing.T) (*gin.Engine, *gorm.DB, *httptest.Server) {
	t.Helper()

	// 创建 mock 上游服务器
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 默认返回成功，由各测试 case 覆盖
		w.WriteHeader(http.StatusOK)
		_, err := w.Write([]byte(`{"error":"not mocked"}`))
		require.NoError(t, err)
	}))

	db := setupTestDB(t)
	t.Cleanup(func() {
		sqlDB, _ := db.DB()
		if sqlDB != nil {
			sqlDB.Close()
		}
		upstream.Close()
	})

	providerRepo := repository.NewProviderRepository(db)
	modelRepo := repository.NewModelRepository(db)
	statsRepo := repository.NewStatsRepository(db)

	logger := zap.NewNop()
	routingCache := service.NewRoutingCache(modelRepo, providerRepo, logger)
	statsBuffer := service.NewStatsBuffer(statsRepo, logger)

	providerService := service.NewProviderService(providerRepo, modelRepo, routingCache)
	modelService := service.NewModelService(modelRepo, providerRepo, routingCache)
	routingService := service.NewRoutingService(routingCache)
	statsService := service.NewStatsService(statsRepo, statsBuffer)

	// 创建 ConversionEngine
	registry := conversion.NewMemoryRegistry()
	require.NoError(t, registry.Register(openaiConv.NewAdapter()))
	require.NoError(t, registry.Register(anthropic.NewAdapter()))
	engine := conversion.NewConversionEngine(registry, logger)

	providerClient := provider.NewClient(logger)
	proxyHandler := handler.NewProxyHandler(engine, providerClient, routingService, providerService, statsService, logger)
	providerHandler := handler.NewProviderHandler(providerService)
	modelHandler := handler.NewModelHandler(modelService)
	statsHandler := handler.NewStatsHandler(statsService)

	_ = modelService

	r := gin.New()
	r.Use(middleware.CORS())

	// 代理路由
	r.Any("/:protocol/*path", proxyHandler.HandleProxy)

	// 管理路由
	providers := r.Group("/api/providers")
	{
		providers.GET("", providerHandler.ListProviders)
		providers.POST("", providerHandler.CreateProvider)
		providers.GET("/:id", providerHandler.GetProvider)
		providers.PUT("/:id", providerHandler.UpdateProvider)
		providers.DELETE("/:id", providerHandler.DeleteProvider)
	}
	models := r.Group("/api/models")
	{
		models.GET("", modelHandler.ListModels)
		models.POST("", modelHandler.CreateModel)
		models.GET("/:id", modelHandler.GetModel)
		models.PUT("/:id", modelHandler.UpdateModel)
		models.DELETE("/:id", modelHandler.DeleteModel)
	}
	_ = statsHandler

	return r, db, upstream
}

// createProviderAndModel 辅助：创建供应商和模型
func createProviderAndModel(t *testing.T, r *gin.Engine, providerID, protocol, modelName string, upstreamURL string) {
	t.Helper()

	providerBody, _ := json.Marshal(map[string]string{
		"id":       providerID,
		"name":     providerID,
		"api_key":  "test-key",
		"base_url": upstreamURL,
		"protocol": protocol,
	})
	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/api/providers", bytes.NewReader(providerBody))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)
	require.Equal(t, 201, w.Code)

	modelBody, _ := json.Marshal(map[string]string{
		"provider_id": providerID,
		"model_name":  modelName,
	})
	w = httptest.NewRecorder()
	req = httptest.NewRequest("POST", "/api/models", bytes.NewReader(modelBody))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)
	require.Equal(t, 201, w.Code)
}

// ============ 跨协议非流式转换测试 ============

func TestConversion_OpenAIToAnthropic_NonStream(t *testing.T) {
	r, _, upstream := setupConversionTest(t)

	// 配置上游返回 Anthropic 格式响应
	upstream.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 验证请求被转换为 Anthropic 格式
		body, err := io.ReadAll(r.Body)
		require.NoError(t, err)
		var req map[string]any
		require.NoError(t, json.Unmarshal(body, &req))

		assert.Equal(t, "/v1/messages", r.URL.Path)
		assert.Contains(t, r.Header.Get("Content-Type"), "application/json")

		// 返回 Anthropic 响应
		resp := map[string]any{
			"id":    "msg_test",
			"type":  "message",
			"role":  "assistant",
			"model": "anthropic_p/claude-3-opus",
			"content": []map[string]any{
				{"type": "text", "text": "Hello from Anthropic!"},
			},
			"stop_reason": "end_turn",
			"usage": map[string]any{
				"input_tokens":  10,
				"output_tokens": 20,
			},
		}
		w.Header().Set("Content-Type", "application/json")
		require.NoError(t, json.NewEncoder(w).Encode(resp))
	})

	createProviderAndModel(t, r, "anthropic_p", "anthropic", "claude-3-opus", upstream.URL)

	// 使用 OpenAI 格式发送请求
	openaiReq := map[string]any{
		"model": "anthropic_p/claude-3-opus",
		"messages": []map[string]any{
			{"role": "user", "content": "Hello"},
		},
		"stream": false,
	}
	body, _ := json.Marshal(openaiReq)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)

	var resp map[string]any
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "chat.completion", resp["object"])

	choices, ok := resp["choices"].([]any)
	require.True(t, ok)
	require.Len(t, choices, 1)
	choice, ok := choices[0].(map[string]any)
	require.True(t, ok)
	msg, ok := choice["message"].(map[string]any)
	require.True(t, ok)
	assert.Contains(t, msg["content"], "Hello from Anthropic!")
}

func TestConversion_AnthropicToOpenAI_NonStream(t *testing.T) {
	r, _, upstream := setupConversionTest(t)

	upstream.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, err := io.ReadAll(r.Body)
		require.NoError(t, err)
		var req map[string]any
		require.NoError(t, json.Unmarshal(body, &req))

		assert.Equal(t, "/chat/completions", r.URL.Path)
		assert.Contains(t, r.Header.Get("Authorization"), "Bearer test-key")

		resp := map[string]any{
			"id":      "chatcmpl-test",
			"object":  "chat.completion",
			"model":   "gpt-4",
			"created": time.Now().Unix(),
			"choices": []map[string]any{
				{
					"index":         0,
					"message":       map[string]any{"role": "assistant", "content": "Hello from OpenAI!"},
					"finish_reason": "stop",
				},
			},
			"usage": map[string]any{
				"prompt_tokens":     10,
				"completion_tokens": 20,
				"total_tokens":      30,
			},
		}
		w.Header().Set("Content-Type", "application/json")
		require.NoError(t, json.NewEncoder(w).Encode(resp))
	})

	createProviderAndModel(t, r, "openai_p", "openai", "gpt-4", upstream.URL)

	anthropicReq := map[string]any{
		"model":      "openai_p/gpt-4",
		"max_tokens": 1024,
		"messages": []map[string]any{
			{"role": "user", "content": "Hello"},
		},
		"stream": false,
	}
	body, _ := json.Marshal(anthropicReq)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/anthropic/v1/messages", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)

	var resp map[string]any
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Equal(t, "message", resp["type"])

	content, ok := resp["content"].([]any)
	require.True(t, ok)
	require.Len(t, content, 1)
	block, ok2 := content[0].(map[string]any)
	require.True(t, ok2)
	assert.Contains(t, block["text"], "Hello from OpenAI!")
}

// ============ 同协议透传测试 ============

func TestConversion_OpenAIToOpenAI_Passthrough(t *testing.T) {
	r, _, upstream := setupConversionTest(t)

	upstream.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, "/chat/completions", r.URL.Path)

		body, err := io.ReadAll(r.Body)
		require.NoError(t, err)
		var req map[string]any
		require.NoError(t, json.Unmarshal(body, &req))
		// Smart Passthrough: 请求体中的统一 ID 应被改写为上游模型名
		assert.Equal(t, "gpt-4", req["model"])

		w.Header().Set("Content-Type", "application/json")
		// 上游返回上游模型名
		_, err = w.Write([]byte(`{"id":"chatcmpl-pass","object":"chat.completion","model":"gpt-4","choices":[{"index":0,"message":{"role":"assistant","content":"passthrough"},"finish_reason":"stop"}],"usage":{"prompt_tokens":5,"completion_tokens":1,"total_tokens":6}}`))
		require.NoError(t, err)
	})

	createProviderAndModel(t, r, "openai_p", "openai", "gpt-4", upstream.URL)

	reqBody := map[string]any{
		"model":    "openai_p/gpt-4", // 客户端发送统一 ID
		"messages": []map[string]any{{"role": "user", "content": "test"}},
	}
	body, _ := json.Marshal(reqBody)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)
	// Smart Passthrough: 响应体中的上游模型名应被改写为统一 ID
	assert.Contains(t, w.Body.String(), `"model":"openai_p/gpt-4"`)
}

func TestConversion_AnthropicToAnthropic_Passthrough(t *testing.T) {
	r, _, upstream := setupConversionTest(t)

	upstream.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, "/v1/messages", r.URL.Path)

		body, err := io.ReadAll(r.Body)
		require.NoError(t, err)
		var req map[string]any
		require.NoError(t, json.Unmarshal(body, &req))
		// Smart Passthrough: 请求体中的统一 ID 应被改写为上游模型名
		assert.Equal(t, "claude-3-opus", req["model"])

		// 上游返回上游模型名
		w.Header().Set("Content-Type", "application/json")
		_, err = w.Write([]byte(`{"id":"msg-pass","type":"message","role":"assistant","model":"claude-3-opus","content":[{"type":"text","text":"passthrough"}],"stop_reason":"end_turn","usage":{"input_tokens":5,"output_tokens":1}}`))
		require.NoError(t, err)
	})

	createProviderAndModel(t, r, "anthropic_p", "anthropic", "claude-3-opus", upstream.URL)

	reqBody := map[string]any{
		"model":      "anthropic_p/claude-3-opus", // 客户端发送统一 ID
		"max_tokens": 1024,
		"messages":   []map[string]any{{"role": "user", "content": "test"}},
	}
	body, _ := json.Marshal(reqBody)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/anthropic/v1/messages", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)
	// Smart Passthrough: 响应体中的上游模型名应被改写为统一 ID
	assert.Contains(t, w.Body.String(), `"model":"anthropic_p/claude-3-opus"`)
}

// ============ 流式转换测试 ============

func TestConversion_OpenAIToAnthropic_Stream(t *testing.T) {
	r, _, upstream := setupConversionTest(t)

	upstream.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.WriteHeader(http.StatusOK)

		events := []string{
			"event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_stream\",\"model\":\"claude-3-opus\",\"usage\":{\"input_tokens\":10,\"output_tokens\":0}}}\n\n",
			"event: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\n",
			"event: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hi\"}}\n\n",
			"event: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\n",
			"event: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"},\"usage\":{\"output_tokens\":5}}\n\n",
			"event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n",
		}
		for _, e := range events {
			_, err := w.Write([]byte(e))
			require.NoError(t, err)
			if f, ok := w.(http.Flusher); ok {
				f.Flush()
			}
		}
	})

	createProviderAndModel(t, r, "anthropic_p", "anthropic", "claude-3-opus", upstream.URL)

	openaiReq := map[string]any{
		"model":    "anthropic_p/claude-3-opus",
		"messages": []map[string]any{{"role": "user", "content": "Hello"}},
		"stream":   true,
	}
	body, _ := json.Marshal(openaiReq)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)
	ct := w.Header().Get("Content-Type")
	assert.Contains(t, ct, "text/event-stream")
}

func TestConversion_AnthropicToOpenAI_Stream(t *testing.T) {
	r, _, upstream := setupConversionTest(t)

	upstream.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.WriteHeader(http.StatusOK)

		events := []string{
			fmt.Sprintf("data: {\"id\":\"chatcmpl-s\",\"object\":\"chat.completion.chunk\",\"model\":\"gpt-4\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\"}}]}\n\n"),
			"data: {\"id\":\"chatcmpl-s\",\"object\":\"chat.completion.chunk\",\"model\":\"gpt-4\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"Hey\"}}]}\n\n",
			"data: {\"id\":\"chatcmpl-s\",\"object\":\"chat.completion.chunk\",\"model\":\"gpt-4\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"!\"}}]}\n\n",
			"data: {\"id\":\"chatcmpl-s\",\"object\":\"chat.completion.chunk\",\"model\":\"gpt-4\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}]}\n\n",
			"data: [DONE]\n\n",
		}
		for _, e := range events {
			_, err := w.Write([]byte(e))
			require.NoError(t, err)
			if f, ok := w.(http.Flusher); ok {
				f.Flush()
			}
		}
	})

	createProviderAndModel(t, r, "openai_p", "openai", "gpt-4", upstream.URL)

	anthropicReq := map[string]any{
		"model":      "openai_p/gpt-4",
		"max_tokens": 1024,
		"messages":   []map[string]any{{"role": "user", "content": "Hello"}},
		"stream":     true,
	}
	body, _ := json.Marshal(anthropicReq)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/anthropic/v1/messages", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)
	ct := w.Header().Get("Content-Type")
	assert.Contains(t, ct, "text/event-stream")
}

// ============ Models 接口测试 ============

func TestConversion_Models_CrossProtocol(t *testing.T) {
	// 测试 Models 接口跨协议转换的编解码逻辑
	// 由于 GET /models 无 body 无法路由，此处测试 adapter 级别的编解码

	registry := conversion.NewMemoryRegistry()
	require.NoError(t, registry.Register(openaiConv.NewAdapter()))
	require.NoError(t, registry.Register(anthropic.NewAdapter()))

	openaiAdapter, _ := registry.Get("openai")
	anthropicAdapter, _ := registry.Get("anthropic")

	// 模拟 OpenAI 格式的 models 响应
	openaiModelsBody := []byte(`{"object":"list","data":[{"id":"gpt-4","object":"model","created":1700000000,"owned_by":"openai"},{"id":"gpt-3.5-turbo","object":"model","created":1700000001,"owned_by":"openai"}]}`)

	// OpenAI decode → Canonical → Anthropic encode
	modelList, err := openaiAdapter.DecodeModelsResponse(openaiModelsBody)
	require.NoError(t, err)
	assert.Len(t, modelList.Models, 2)
	assert.Equal(t, "gpt-4", modelList.Models[0].ID)

	// 编码为 Anthropic 格式
	anthropicBody, err := anthropicAdapter.EncodeModelsResponse(modelList)
	require.NoError(t, err)

	var anthropicResp map[string]any
	require.NoError(t, json.Unmarshal(anthropicBody, &anthropicResp))
	data, okd := anthropicResp["data"].([]any)
	require.True(t, okd)
	assert.Len(t, data, 2)

	first, okf := data[0].(map[string]any)
	require.True(t, okf)
	assert.Equal(t, "gpt-4", first["id"])
	assert.Equal(t, "model", first["type"])

	// 反向测试：Anthropic decode → Canonical → OpenAI encode
	anthropicModelsBody := []byte(`{"data":[{"id":"claude-3-opus","type":"model","display_name":"Claude 3 Opus","created_at":"2025-01-01T00:00:00Z"}],"has_more":false}`)
	modelList2, err := anthropicAdapter.DecodeModelsResponse(anthropicModelsBody)
	require.NoError(t, err)
	assert.Len(t, modelList2.Models, 1)
	assert.Equal(t, "Claude 3 Opus", modelList2.Models[0].Name)

	openaiBody, err := openaiAdapter.EncodeModelsResponse(modelList2)
	require.NoError(t, err)

	var openaiResp map[string]any
	require.NoError(t, json.Unmarshal(openaiBody, &openaiResp))
	oaiData, oki := openaiResp["data"].([]any)
	require.True(t, oki)
	assert.Len(t, oaiData, 1)
	firstOai, okf2 := oaiData[0].(map[string]any)
	require.True(t, okf2)
	assert.Equal(t, "claude-3-opus", firstOai["id"])
}

// ============ 错误响应测试 ============

func TestConversion_ErrorResponse_Format(t *testing.T) {
	r, _, _ := setupConversionTest(t)

	// 请求不存在的模型
	reqBody := map[string]any{
		"model":    "nonexistent",
		"messages": []map[string]any{{"role": "user", "content": "test"}},
	}
	body, _ := json.Marshal(reqBody)

	// OpenAI 协议格式
	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)
	assert.True(t, w.Code >= 400)
}

// ============ 旧路由返回 404 ============

func TestConversion_OldRoutes_Return404(t *testing.T) {
	r, _, _ := setupConversionTest(t)

	// 旧 OpenAI 路由
	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(`{"model":"test"}`))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)
	assert.Equal(t, 404, w.Code)

	// 旧 Anthropic 路由
	w = httptest.NewRecorder()
	req = httptest.NewRequest("POST", "/v1/messages", strings.NewReader(`{"model":"test"}`))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)
	assert.Equal(t, 404, w.Code)
}

// ============ Provider Protocol 字段测试 ============

func TestConversion_ProviderWithProtocol(t *testing.T) {
	r, _, _ := setupConversionTest(t)

	// 创建带 protocol 字段的 provider
	providerBody := map[string]any{
		"id":       "test_protocol",
		"name":     "Test Protocol",
		"api_key":  "sk-test",
		"base_url": "https://test.com",
		"protocol": "anthropic",
	}
	body, _ := json.Marshal(providerBody)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/api/providers", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)
	require.Equal(t, 201, w.Code)

	var created map[string]any
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &created))
	assert.Equal(t, "sk-test", created["api_key"])

	// 获取时应包含 protocol
	w = httptest.NewRecorder()
	req = httptest.NewRequest("GET", "/api/providers/test_protocol", nil)
	r.ServeHTTP(w, req)
	assert.Equal(t, 200, w.Code)

	var fetched map[string]any
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &fetched))
	assert.Equal(t, "anthropic", fetched["protocol"])
}

func TestConversion_ProviderDefaultProtocol(t *testing.T) {
	r, _, _ := setupConversionTest(t)

	// 不指定 protocol，默认应为 openai
	providerBody := map[string]any{
		"id":       "default_proto",
		"name":     "Default",
		"api_key":  "sk-test",
		"base_url": "https://test.com",
	}
	body, _ := json.Marshal(providerBody)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/api/providers", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(w, req)
	require.Equal(t, 201, w.Code)

	var created map[string]any
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &created))
	assert.Equal(t, "openai", created["protocol"])
}

// Suppress unused imports
var (
	_ = fmt.Sprintf
	_ = strings.Contains
	_ = time.Second
)
