package httpapi

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

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

func prepareCompatibleStream(w http.ResponseWriter) http.Flusher {
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	flusher, _ := w.(http.Flusher)
	return flusher
}

type compatibleStreamWriter struct {
	kind         string
	model        string
	includeUsage bool

	id                string
	created           int64
	systemFingerprint any
	sentRole          bool
	sentFinish        bool
	sentUsage         bool
	responseSequence  int
	sentResponseDone  bool
}

func newCompatibleStreamWriter(kind string, model string, includeUsage bool) *compatibleStreamWriter {
	return &compatibleStreamWriter{
		kind:         kind,
		model:        model,
		includeUsage: includeUsage,
		id:           "chatcmpl-stream",
		created:      time.Now().Unix(),
	}
}

func (s *compatibleStreamWriter) writeDelta(w http.ResponseWriter, event clients.StreamDeltaEvent) {
	if s.kind == "responses" {
		if event.Event != nil {
			eventType, _ := event.Event["type"].(string)
			if eventType != "" {
				if sequence := intFromStreamValue(event.Event["sequence_number"]); sequence >= s.responseSequence {
					s.responseSequence = sequence + 1
				}
				if isResponsesTerminalEvent(eventType) {
					s.sentResponseDone = true
				}
				sendSSE(w, eventType, event.Event)
				return
			}
		}
		if event.Text != "" {
			sendSSE(w, "response.output_text.delta", map[string]any{"type": "response.output_text.delta", "delta": event.Text})
		}
		return
	}

	if event.Event != nil && isChatCompletionChunk(event.Event) {
		s.writeChatChunk(w, event.Event)
		return
	}

	if event.Text == "" && event.ReasoningContent == "" {
		return
	}
	s.ensureRoleChunk(w)
	if event.ReasoningContent != "" {
		s.writeChatData(w, s.chatChunk([]any{map[string]any{"index": 0, "delta": map[string]any{"reasoning_content": event.ReasoningContent}, "finish_reason": nil}}, nil))
	}
	if event.Text != "" {
		s.writeChatData(w, s.chatChunk([]any{map[string]any{"index": 0, "delta": map[string]any{"content": event.Text}, "finish_reason": nil}}, nil))
	}
}

func (s *compatibleStreamWriter) writeDone(w http.ResponseWriter, output map[string]any) {
	if s.kind == "responses" {
		if s.sentResponseDone {
			return
		}
		eventType := responsesTerminalEvent(output)
		sendSSE(w, eventType, map[string]any{"type": eventType, "sequence_number": s.responseSequence, "response": output})
		s.sentResponseDone = true
		return
	}
	s.captureOutputMetadata(output)
	if !s.sentRole {
		s.ensureRoleChunk(w)
	}
	if !s.sentFinish {
		s.writeChatData(w, s.chatChunk([]any{map[string]any{"index": 0, "delta": map[string]any{}, "finish_reason": finishReasonFromOutput(output)}}, nil))
		s.sentFinish = true
	}
	if s.includeUsage && !s.sentUsage {
		if usage, ok := output["usage"].(map[string]any); ok && len(usage) > 0 {
			usage = clients.NormalizeChatCompletionUsage(usage)
			s.writeChatData(w, s.chatChunk([]any{}, usage))
			s.sentUsage = true
		}
	}
	s.writeDoneMarker(w)
}

func isResponsesTerminalEvent(eventType string) bool {
	switch eventType {
	case "response.completed", "response.incomplete", "response.failed", "response.cancelled":
		return true
	default:
		return false
	}
}

func responsesTerminalEvent(output map[string]any) string {
	switch status, _ := output["status"].(string); status {
	case "incomplete":
		return "response.incomplete"
	case "failed":
		return "response.failed"
	case "cancelled":
		return "response.cancelled"
	default:
		return "response.completed"
	}
}

func intFromStreamValue(value any) int {
	switch typed := value.(type) {
	case int:
		return typed
	case int64:
		return int(typed)
	case float64:
		return int(typed)
	default:
		return -1
	}
}

func (s *compatibleStreamWriter) writeChatChunk(w http.ResponseWriter, chunk map[string]any) {
	chunk = clients.NormalizeChatCompletionStreamEvent(chunk)
	s.captureChunkMetadata(chunk)
	choices, _ := chunk["choices"].([]any)
	usage, hasUsage := chunk["usage"].(map[string]any)
	if len(choices) == 0 && hasUsage {
		if !s.includeUsage {
			return
		}
		s.writeChatData(w, s.chatChunk([]any{}, usage))
		s.sentUsage = true
		return
	}
	if len(choices) > 0 && !chunkHasRole(choices) && !s.sentRole {
		s.ensureRoleChunk(w)
	}
	if chunkHasRole(choices) {
		s.sentRole = true
	}
	if chunkHasFinishReason(choices) {
		s.sentFinish = true
	}
	normalized := cloneMap(chunk)
	normalized["id"] = s.id
	normalized["object"] = "chat.completion.chunk"
	normalized["created"] = s.created
	normalized["model"] = firstString(normalized["model"], s.model)
	normalized["system_fingerprint"] = s.systemFingerprint
	s.writeChatData(w, normalized)
}

func (s *compatibleStreamWriter) ensureRoleChunk(w http.ResponseWriter) {
	if s.sentRole {
		return
	}
	s.writeChatData(w, s.chatChunk([]any{map[string]any{"index": 0, "delta": map[string]any{"role": "assistant"}, "finish_reason": nil}}, nil))
	s.sentRole = true
}

func (s *compatibleStreamWriter) chatChunk(choices []any, usage map[string]any) map[string]any {
	chunk := map[string]any{
		"id":                 s.id,
		"object":             "chat.completion.chunk",
		"created":            s.created,
		"model":              s.model,
		"system_fingerprint": s.systemFingerprint,
		"choices":            choices,
	}
	if usage != nil {
		chunk["usage"] = usage
	} else {
		chunk["usage"] = nil
	}
	return chunk
}

func (s *compatibleStreamWriter) writeChatData(w http.ResponseWriter, payload map[string]any) {
	bytes, _ := json.Marshal(payload)
	_, _ = fmt.Fprintf(w, "data: %s\n\n", bytes)
}

func (s *compatibleStreamWriter) writeDoneMarker(w http.ResponseWriter) {
	_, _ = fmt.Fprint(w, "data: [DONE]\n\n")
}

func (s *compatibleStreamWriter) captureChunkMetadata(chunk map[string]any) {
	if id := firstString(chunk["id"], ""); id != "" {
		s.id = id
	}
	if model := firstString(chunk["model"], ""); model != "" {
		s.model = model
	}
	if created := int64FromAny(chunk["created"]); created > 0 {
		s.created = created
	}
	if value, ok := chunk["system_fingerprint"]; ok {
		s.systemFingerprint = value
	}
}

func (s *compatibleStreamWriter) captureOutputMetadata(output map[string]any) {
	if id := firstString(output["id"], ""); id != "" {
		s.id = id
	}
	if model := firstString(output["model"], ""); model != "" {
		s.model = model
	}
	if created := int64FromAny(output["created"]); created > 0 {
		s.created = created
	}
	if value, ok := output["system_fingerprint"]; ok {
		s.systemFingerprint = value
	}
}

func writeCompatibleStream(w http.ResponseWriter, kind string, model string, output map[string]any) {
	prepareCompatibleStream(w)
	writer := newCompatibleStreamWriter(kind, model, true)
	content := extractOutputText(output)
	if content == "" {
		content = "done"
	}
	writer.writeDelta(w, clients.StreamDeltaEvent{Text: content})
	writer.writeDone(w, output)
}

func firstString(value any, fallback string) string {
	if text, ok := value.(string); ok && text != "" {
		return text
	}
	return fallback
}

func int64FromAny(value any) int64 {
	switch typed := value.(type) {
	case int64:
		return typed
	case int:
		return int64(typed)
	case float64:
		return int64(typed)
	default:
		return 0
	}
}

func isChatCompletionChunk(event map[string]any) bool {
	object, _ := event["object"].(string)
	if object == "chat.completion.chunk" {
		return true
	}
	_, hasChoices := event["choices"].([]any)
	return hasChoices
}

func chunkHasRole(choices []any) bool {
	for _, rawChoice := range choices {
		choice, _ := rawChoice.(map[string]any)
		delta, _ := choice["delta"].(map[string]any)
		if role, ok := delta["role"].(string); ok && role != "" {
			return true
		}
	}
	return false
}

func chunkHasFinishReason(choices []any) bool {
	for _, rawChoice := range choices {
		choice, _ := rawChoice.(map[string]any)
		if reason, ok := choice["finish_reason"].(string); ok && reason != "" {
			return true
		}
	}
	return false
}

func finishReasonFromOutput(output map[string]any) string {
	choices, _ := output["choices"].([]any)
	for _, rawChoice := range choices {
		choice, _ := rawChoice.(map[string]any)
		if reason, ok := choice["finish_reason"].(string); ok && reason != "" {
			return reason
		}
	}
	return "stop"
}

func cloneMap(value map[string]any) map[string]any {
	out := map[string]any{}
	for key, item := range value {
		out[key] = item
	}
	return out
}

func extractOutputText(output map[string]any) string {
	if text, ok := output["output_text"].(string); ok {
		return text
	}
	choices, _ := output["choices"].([]any)
	for _, rawChoice := range choices {
		choice, _ := rawChoice.(map[string]any)
		message, _ := choice["message"].(map[string]any)
		if content, ok := message["content"].(string); ok {
			return content
		}
	}
	return ""
}
