package dashboard

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"

	"github.com/gin-gonic/gin"
)

const dashboardStreamReadBufferBytes = 64 * 1024

func startDashboardEventStream(c *gin.Context) {
	c.Writer.Header().Set("Content-Type", "text/event-stream")
	c.Writer.Header().Set("Cache-Control", "no-cache")
	c.Writer.Header().Set("Connection", "keep-alive")
	c.Writer.Header().Set("X-Accel-Buffering", "no")
	c.Writer.Flush()
}

func streamDashboardPipe(c *gin.Context, r io.Reader, handleLine func(*gin.Context, string)) {
	reader := bufio.NewReaderSize(r, dashboardStreamReadBufferBytes)
	for {
		line, isPrefix, err := reader.ReadLine()
		if len(line) > 0 {
			if isPrefix {
				streamDashboardLongLogLine(c, reader, line)
				if err != nil {
					return
				}
				continue
			}
			handleLine(c, string(line))
		}
		if err != nil {
			return
		}
	}
}

func streamDashboardLongLogLine(c *gin.Context, reader *bufio.Reader, first []byte) {
	writeDashboardLogEvent(c, string(first))
	for {
		chunk, isPrefix, err := reader.ReadLine()
		if len(chunk) > 0 {
			writeDashboardLogEvent(c, string(chunk))
		}
		if err != nil || !isPrefix {
			return
		}
	}
}

func writeDashboardOperationStreamLine(c *gin.Context, line string) {
	if line == "" {
		return
	}

	var msg map[string]interface{}
	if err := json.Unmarshal([]byte(line), &msg); err == nil {
		if msgType, ok := msg["_type"].(string); ok {
			switch msgType {
			case "done":
				writeDashboardSSE(c, "done", `{"success":true}`)
				return
			case "error":
				if errMsg, ok := msg["message"].(string); ok {
					writeDashboardSSE(c, "error", fmt.Sprintf(`{"message":"%s"}`, escapeJSON(errMsg)))
				}
				return
			}
		}
	}

	writeDashboardLogEvent(c, line)
}

func writeDashboardLogEvent(c *gin.Context, text string) {
	if text == "" {
		return
	}
	writeDashboardSSE(c, "log", fmt.Sprintf(`{"text":"%s"}`, escapeJSON(text)))
}

func writeDashboardSSE(c *gin.Context, event string, data string) {
	fmt.Fprintf(c.Writer, "event: %s\ndata: %s\n\n", event, data)
	c.Writer.Flush()
}

func escapeJSON(s string) string {
	b, _ := json.Marshal(s)
	return string(b[1 : len(b)-1]) // strip quotes
}
