package websocket

import (
	"bufio"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	internalDb "server/internal/db"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/websocket/v2"
	"gorm.io/gorm"
)

const (
	// Heartbeat kiểu game: phát hiện mất kết nối trong ~30–45s.
	wsPingInterval = 15 * time.Second
	wsPongWait     = 45 * time.Second
	// Grace khi SV reconnect — tránh nhấp nháy online/offline.
	studentOfflineGrace = 8 * time.Second
)

type SocketMsg struct {
	Event string         `json:"event"`
	Data  map[string]any `json:"data"`
}

type SocketClient struct {
	Conn      *websocket.Conn
	StudentID int64
	ClassID   int64
	StaffID   uint
	Role      string // "student" | "teacher"
	Addr      string
	writeMu   sync.Mutex
}

func (c *SocketClient) WriteJSON(v any) error {
	c.writeMu.Lock()
	defer c.writeMu.Unlock()
	return c.Conn.WriteJSON(v)
}

func (c *SocketClient) WriteRaw(msg []byte) error {
	c.writeMu.Lock()
	defer c.writeMu.Unlock()
	return c.Conn.WriteMessage(websocket.TextMessage, msg)
}

type offlineGrace struct {
	until   time.Time
	classID int64
}

type WsHub struct {
	mu                    sync.RWMutex
	students              map[int64]*SocketClient
	teachers              map[string]*SocketClient
	teachersByStaff       map[uint][]string  // staffId -> teacher connection addresses
	subscribers           map[int64][]string // studentId -> list of teacher connection addresses
	grace                 map[int64]offlineGrace
	graceTimers           map[int64]*time.Timer
	subscriberModes       map[string]string    // "teacherAddr_studentId" -> "grid"|"focus"
	lastRelayed           map[string]time.Time // "teacherAddr_studentId_event" -> time
	httpScreenSubscribers map[int64][]chan []byte // studentId -> list of channels for screen MJPEG
	httpWebcamSubscribers map[int64][]chan []byte // studentId -> list of channels for webcam MJPEG
}

var Hub = &WsHub{
	students:              make(map[int64]*SocketClient),
	teachers:              make(map[string]*SocketClient),
	teachersByStaff:       make(map[uint][]string),
	subscribers:           make(map[int64][]string),
	grace:                 make(map[int64]offlineGrace),
	graceTimers:           make(map[int64]*time.Timer),
	subscriberModes:       make(map[string]string),
	lastRelayed:           make(map[string]time.Time),
	httpScreenSubscribers: make(map[int64][]chan []byte),
	httpWebcamSubscribers: make(map[int64][]chan []byte),
}

func (h *WsHub) IsStudentOnline(studentRkID int64) bool {
	h.mu.RLock()
	defer h.mu.RUnlock()
	if _, ok := h.students[studentRkID]; ok {
		return true
	}
	g, ok := h.grace[studentRkID]
	return ok && time.Now().Before(g.until)
}

func (h *WsHub) PushExamToStudent(studentRkID int64, data map[string]any) {
	h.mu.RLock()
	client, ok := h.students[studentRkID]
	h.mu.RUnlock()
	if !ok || client == nil {
		return
	}
	_ = client.WriteJSON(SocketMsg{Event: "exam:paper-sent", Data: data})
}

func (h *WsHub) PushChatToStudent(studentRkID int64, data map[string]any) {
	h.mu.RLock()
	client, ok := h.students[studentRkID]
	h.mu.RUnlock()
	if !ok || client == nil {
		return
	}
	_ = client.WriteJSON(SocketMsg{Event: "chat:message", Data: data})
}

func (h *WsHub) PushChatToStaff(staffID uint, data map[string]any) {
	if data == nil {
		data = map[string]any{}
	}
	data["targetStaffId"] = staffID
	msg := SocketMsg{Event: "chat:message", Data: data}
	h.mu.RLock()
	defer h.mu.RUnlock()
	for _, t := range h.teachers {
		if t != nil && t.Role == "teacher" {
			_ = t.WriteJSON(msg)
		}
	}
}

func (h *WsHub) BroadcastStudentViolation(data map[string]any) {
	msg := SocketMsg{Event: "teacher:student-violation", Data: data}
	msgBytes, err := json.Marshal(msg)
	if err != nil {
		return
	}
	h.mu.RLock()
	defer h.mu.RUnlock()
	for _, t := range h.teachers {
		if t != nil {
			_ = t.WriteRaw(msgBytes)
		}
	}
}

func (h *WsHub) broadcastPresence(studentID int64, online bool, classID int64) {
	msg := SocketMsg{
		Event: "presence:update",
		Data: map[string]any{
			"studentId": studentID,
			"online":    online,
			"classId":   classID,
		},
	}
	msgBytes, err := json.Marshal(msg)
	if err != nil {
		return
	}
	for _, t := range h.teachers {
		if t != nil {
			_ = t.WriteRaw(msgBytes)
		}
	}
}

// ForceStudentOffline — tắt ngay (không chờ grace), dùng khi SV cố ý đóng app / vi phạm.
func (h *WsHub) ForceStudentOffline(studentID int64) {
	h.mu.Lock()
	defer h.mu.Unlock()

	classID := int64(0)
	if client, ok := h.students[studentID]; ok && client != nil {
		classID = client.ClassID
		delete(h.students, studentID)
	}
	if g, ok := h.grace[studentID]; ok {
		if classID == 0 {
			classID = g.classID
		}
	}
	h.cancelGraceLocked(studentID)

	log.Printf("[WS] Student %d force offline (intentional quit/violation)", studentID)
	h.broadcastPresence(studentID, false, classID)
	if teachers, exists := h.subscribers[studentID]; exists {
		for _, tAddr := range teachers {
			if t, found := h.teachers[tAddr]; found {
				_ = t.WriteJSON(SocketMsg{
					Event: "teacher:stream-stopped",
					Data:  map[string]any{"studentId": studentID},
				})
			}
		}
	}
}

func (h *WsHub) cancelGraceLocked(studentID int64) {
	if t, ok := h.graceTimers[studentID]; ok {
		t.Stop()
		delete(h.graceTimers, studentID)
	}
	delete(h.grace, studentID)
}

func (h *WsHub) finalizeStudentOffline(studentID int64, classID int64) {
	h.mu.Lock()
	defer h.mu.Unlock()

	if _, online := h.students[studentID]; online {
		return
	}
	if _, ok := h.grace[studentID]; !ok {
		return
	}
	delete(h.grace, studentID)
	delete(h.graceTimers, studentID)

	log.Printf("[WS] Student %d offline after grace", studentID)
	h.broadcastPresence(studentID, false, classID)

	if teachers, exists := h.subscribers[studentID]; exists {
		for _, tAddr := range teachers {
			if t, found := h.teachers[tAddr]; found {
				_ = t.WriteJSON(SocketMsg{
					Event: "teacher:stream-stopped",
					Data:  map[string]any{"studentId": studentID},
				})
			}
		}
	}
}

func (h *WsHub) GetOnlineStudentIDs(classID int64) []int64 {
	h.mu.RLock()
	defer h.mu.RUnlock()

	seen := map[int64]bool{}
	var ids []int64
	now := time.Now()
	for _, client := range h.students {
		if client.ClassID == classID {
			seen[client.StudentID] = true
			ids = append(ids, client.StudentID)
		}
	}
	for sid, g := range h.grace {
		if now.Before(g.until) && g.classID == classID && !seen[sid] {
			ids = append(ids, sid)
		}
	}
	if ids == nil {
		return []int64{}
	}
	return ids
}

func (h *WsHub) Register(c *SocketClient) {
	h.mu.Lock()
	defer h.mu.Unlock()

	c.Addr = c.Conn.RemoteAddr().String()
	if c.Role == "student" {
		wasInGrace := false
		if _, ok := h.grace[c.StudentID]; ok {
			wasInGrace = true
		}
		h.cancelGraceLocked(c.StudentID)
		h.students[c.StudentID] = c
		log.Printf("[WS] Student %d registered (Address: %s, Class: %d, reconnectGrace=%v)", c.StudentID, c.Addr, c.ClassID, wasInGrace)
		h.broadcastPresence(c.StudentID, true, c.ClassID)
		if subs, exists := h.subscribers[c.StudentID]; exists && len(subs) > 0 {
			_ = c.WriteJSON(SocketMsg{Event: "start_screenshot_stream"})
			_ = c.WriteJSON(SocketMsg{Event: "start_webcam_stream"})
			log.Printf("[WS] Student %d has active subscribers. Sent start stream commands.", c.StudentID)
		}
	} else if c.Role == "teacher" {
		h.teachers[c.Addr] = c
		if c.StaffID > 0 {
			h.teachersByStaff[c.StaffID] = append(h.teachersByStaff[c.StaffID], c.Addr)
		}
		log.Printf("[WS] Teacher registered (Address: %s, Staff: %d)", c.Addr, c.StaffID)
	}
}

func (h *WsHub) Unregister(c *SocketClient) {
	h.mu.Lock()
	defer h.mu.Unlock()

	if c.Role == "student" {
		current, exists := h.students[c.StudentID]
		if !exists || current != c {
			log.Printf("[WS] Student %d stale disconnect ignored (replaced by newer connection)", c.StudentID)
			return
		}
		delete(h.students, c.StudentID)
		classID := c.ClassID
		studentID := c.StudentID

		h.cancelGraceLocked(studentID)
		deadline := time.Now().Add(studentOfflineGrace)
		h.grace[studentID] = offlineGrace{until: deadline, classID: classID}
		h.graceTimers[studentID] = time.AfterFunc(studentOfflineGrace, func() {
			h.finalizeStudentOffline(studentID, classID)
		})
		log.Printf("[WS] Student %d disconnected — grace %v before offline", studentID, studentOfflineGrace)
		// Chưa broadcast offline / stream-stopped — chờ grace (reconnect nhanh như game).
	} else if c.Role == "teacher" {
		delete(h.teachers, c.Addr)
		if c.StaffID > 0 {
			list := h.teachersByStaff[c.StaffID]
			next := list[:0]
			for _, addr := range list {
				if addr != c.Addr {
					next = append(next, addr)
				}
			}
			if len(next) == 0 {
				delete(h.teachersByStaff, c.StaffID)
			} else {
				h.teachersByStaff[c.StaffID] = next
			}
		}
		log.Printf("[WS] Teacher %s disconnected", c.Addr)

		for sID, teachersList := range h.subscribers {
			newList := []string{}
			for _, addr := range teachersList {
				if addr != c.Addr {
					newList = append(newList, addr)
				}
			}
			// Clean up mode map for this student and teacher
			key := c.Addr + "_" + strconv.FormatInt(sID, 10)
			delete(h.subscriberModes, key)
			delete(h.lastRelayed, key+"_screenshot_stream_frame")
			delete(h.lastRelayed, key+"_webcam_stream_frame")

			if len(newList) == 0 {
				delete(h.subscribers, sID)
				if student, exists := h.students[sID]; exists {
					_ = student.WriteJSON(SocketMsg{Event: "stop_screenshot_stream"})
					_ = student.WriteJSON(SocketMsg{Event: "stop_webcam_stream"})
				}
			} else {
				h.subscribers[sID] = newList
			}
		}
	}
}

func (h *WsHub) Subscribe(teacherAddr string, studentID int64, mode string) {
	h.mu.Lock()
	defer h.mu.Unlock()

	teachersList := h.subscribers[studentID]
	alreadySubscribed := false
	for _, addr := range teachersList {
		if addr == teacherAddr {
			alreadySubscribed = true
			break
		}
	}
	if !alreadySubscribed {
		h.subscribers[studentID] = append(teachersList, teacherAddr)
		log.Printf("[WS] Teacher %s subscribed to student %d stream in %s mode", teacherAddr, studentID, mode)
	}

	key := teacherAddr + "_" + strconv.FormatInt(studentID, 10)
	h.subscriberModes[key] = mode

	if student, exists := h.students[studentID]; exists {
		_ = student.WriteJSON(SocketMsg{Event: "start_screenshot_stream"})
		_ = student.WriteJSON(SocketMsg{Event: "start_webcam_stream"})
	}
}

func (h *WsHub) Unsubscribe(teacherAddr string, studentID int64) {
	h.mu.Lock()
	defer h.mu.Unlock()

	key := teacherAddr + "_" + strconv.FormatInt(studentID, 10)
	delete(h.subscriberModes, key)
	delete(h.lastRelayed, key+"_screenshot_stream_frame")
	delete(h.lastRelayed, key+"_webcam_stream_frame")

	teachersList, exists := h.subscribers[studentID]
	if !exists {
		return
	}

	newList := []string{}
	for _, addr := range teachersList {
		if addr != teacherAddr {
			newList = append(newList, addr)
		}
	}

	if len(newList) == 0 {
		delete(h.subscribers, studentID)
		log.Printf("[WS] Student %d has no more proctor subscribers. Stopping streams.", studentID)
		if student, exists := h.students[studentID]; exists {
			_ = student.WriteJSON(SocketMsg{Event: "stop_screenshot_stream"})
			_ = student.WriteJSON(SocketMsg{Event: "stop_webcam_stream"})
		}
	} else {
		h.subscribers[studentID] = newList
	}
}

func (h *WsHub) RelayFrameRaw(studentID int64, event string, rawImageBuffer json.RawMessage) {
	h.mu.Lock()
	defer h.mu.Unlock()

	// Broadcast to HTTP subscribers if any
	if event == "screenshot_stream_frame" {
		subs, exists := h.httpScreenSubscribers[studentID]
		if exists && len(subs) > 0 {
			var b64Str string
			if err := json.Unmarshal(rawImageBuffer, &b64Str); err == nil && len(b64Str) > 0 {
				if idx := strings.Index(b64Str, ","); idx != -1 {
					b64Str = b64Str[idx+1:]
				}
				if rawBytes, err := base64.StdEncoding.DecodeString(b64Str); err == nil {
					for _, ch := range subs {
						select {
						case ch <- rawBytes:
						default:
						}
					}
				}
			}
		}
	} else if event == "webcam_stream_frame" {
		subs, exists := h.httpWebcamSubscribers[studentID]
		if exists && len(subs) > 0 {
			var b64Str string
			if err := json.Unmarshal(rawImageBuffer, &b64Str); err == nil && len(b64Str) > 0 {
				if idx := strings.Index(b64Str, ","); idx != -1 {
					b64Str = b64Str[idx+1:]
				}
				if rawBytes, err := base64.StdEncoding.DecodeString(b64Str); err == nil {
					for _, ch := range subs {
						select {
						case ch <- rawBytes:
						default:
						}
					}
				}
			}
		}
	}

	teachersList, exists := h.subscribers[studentID]
	if !exists || len(teachersList) == 0 {
		return
	}

	relayEvent := "teacher:screenshot-stream-frame"
	if event == "webcam_stream_frame" {
		relayEvent = "teacher:webcam-stream-frame"
	}

	type RelayFrameRawMsg struct {
		Event string `json:"event"`
		Data  struct {
			StudentID   int64           `json:"studentId"`
			ImageBuffer json.RawMessage `json:"imageBuffer"`
		} `json:"data"`
	}

	msg := RelayFrameRawMsg{
		Event: relayEvent,
	}
	msg.Data.StudentID = studentID
	msg.Data.ImageBuffer = rawImageBuffer

	msgBytes, err := json.Marshal(msg)
	if err != nil {
		return
	}

	now := time.Now()
	for _, addr := range teachersList {
		t, found := h.teachers[addr]
		if !found || t == nil {
			continue
		}

		subKey := addr + "_" + strconv.FormatInt(studentID, 10)
		mode := h.subscriberModes[subKey]

		if mode == "grid" {
			// In grid mode, rate limit to max 1 frame per 3 seconds per stream type
			relayKey := subKey + "_" + event
			lastTime, ok := h.lastRelayed[relayKey]
			if ok && now.Sub(lastTime) < 3*time.Second {
				continue
			}
			h.lastRelayed[relayKey] = now
		}

		// Send asynchronously to avoid head-of-line blocking on slower clients
		go func(client *SocketClient, data []byte) {
			if err := client.WriteRaw(data); err != nil {
				log.Printf("[WS] Relay to teacher %s failed: %v", client.Addr, err)
				_ = client.Conn.Close()
			}
		}(t, msgBytes)
	}
}

func WebSocketHandler(db *gorm.DB) func(*websocket.Conn) {
	return func(c *websocket.Conn) {
		role := c.Query("role", "student")
		studentIDStr := c.Query("studentId", "0")
		classIDStr := c.Query("classId", "0")
		staffIDStr := c.Query("staffId", "0")

		studentID, _ := strconv.ParseInt(studentIDStr, 10, 64)
		classID, _ := strconv.ParseInt(classIDStr, 10, 64)
		staffID64, _ := strconv.ParseUint(staffIDStr, 10, 64)

		if role == "student" && db != nil {
			resolvedClassID := internalDb.FindActiveClassForStudent(db, studentID)
			if resolvedClassID > 0 {
				classID = resolvedClassID
			}
		}

		client := &SocketClient{
			Conn:      c,
			StudentID: studentID,
			ClassID:   classID,
			StaffID:   uint(staffID64),
			Role:      role,
		}

		Hub.Register(client)
		defer func() {
			Hub.Unregister(client)
			c.Close()
		}()

		c.SetReadDeadline(time.Now().Add(wsPongWait))
		c.SetPongHandler(func(string) error {
			return c.SetReadDeadline(time.Now().Add(wsPongWait))
		})

		pingDone := make(chan struct{})
		defer close(pingDone)
		go func() {
			ticker := time.NewTicker(wsPingInterval)
			defer ticker.Stop()
			for {
				select {
				case <-pingDone:
					return
				case <-ticker.C:
					client.writeMu.Lock()
					err := c.WriteControl(websocket.PingMessage, []byte("ping"), time.Now().Add(5*time.Second))
					client.writeMu.Unlock()
					if err != nil {
						return
					}
				}
			}
		}()

		for {
			_, msgBytes, err := c.ReadMessage()
			if err != nil {
				break
			}
			_ = c.SetReadDeadline(time.Now().Add(wsPongWait))

			type EventOnlyMsg struct {
				Event string `json:"event"`
			}
			var eventMsg EventOnlyMsg
			if err := json.Unmarshal(msgBytes, &eventMsg); err != nil {
				continue
			}

			if eventMsg.Event == "screenshot_stream_frame" || eventMsg.Event == "webcam_stream_frame" {
				var frameMsg struct {
					Data struct {
						ImageBuffer json.RawMessage `json:"imageBuffer"`
					} `json:"data"`
				}
				if err := json.Unmarshal(msgBytes, &frameMsg); err == nil && len(frameMsg.Data.ImageBuffer) > 0 {
					// Copy slice to avoid memory race / corruption since WebSocket read buffer gets recycled
					bufCopy := make([]byte, len(frameMsg.Data.ImageBuffer))
					copy(bufCopy, frameMsg.Data.ImageBuffer)
					Hub.RelayFrameRaw(client.StudentID, eventMsg.Event, json.RawMessage(bufCopy))
				}
				continue
			}

			var msg SocketMsg
			if err := json.Unmarshal(msgBytes, &msg); err != nil {
				continue
			}

			switch msg.Event {
			case "client:ping":
				_ = client.WriteJSON(SocketMsg{Event: "client:pong", Data: map[string]any{"t": time.Now().UnixMilli()}})

			case "teacher:subscribe":
				if client.Role == "teacher" {
					if sIDVal, ok := msg.Data["studentId"]; ok {
						var sID int64
						switch v := sIDVal.(type) {
						case float64:
							sID = int64(v)
						case string:
							sID, _ = strconv.ParseInt(v, 10, 64)
						}

						// Nhận chế độ subscription (mặc định là "focus")
						mode := "focus"
						if mVal, ok := msg.Data["mode"].(string); ok && mVal != "" {
							mode = mVal
						}

						if sID > 0 {
							Hub.Subscribe(client.Addr, sID, mode)
						}
					}
				}

			case "teacher:unsubscribe":
				if client.Role == "teacher" {
					if sIDVal, ok := msg.Data["studentId"]; ok {
						var sID int64
						switch v := sIDVal.(type) {
						case float64:
							sID = int64(v)
						case string:
							sID, _ = strconv.ParseInt(v, 10, 64)
						}
						if sID > 0 {
							Hub.Unsubscribe(client.Addr, sID)
						}
					}
				}
			}
		}
	}
}

func (h *WsHub) RegisterHttpSubscriber(studentID int64, kind string) chan []byte {
	h.mu.Lock()
	defer h.mu.Unlock()

	ch := make(chan []byte, 16)
	if kind == "screen" {
		h.httpScreenSubscribers[studentID] = append(h.httpScreenSubscribers[studentID], ch)
	} else if kind == "webcam" {
		h.httpWebcamSubscribers[studentID] = append(h.httpWebcamSubscribers[studentID], ch)
	}

	// Always trigger streams start if there's any HTTP subscriber
	if student, exists := h.students[studentID]; exists {
		_ = student.WriteJSON(SocketMsg{Event: "start_screenshot_stream"})
		_ = student.WriteJSON(SocketMsg{Event: "start_webcam_stream"})
	}

	return ch
}

func (h *WsHub) UnregisterHttpSubscriber(studentID int64, kind string, ch chan []byte) {
	h.mu.Lock()
	defer h.mu.Unlock()

	if kind == "screen" {
		subs := h.httpScreenSubscribers[studentID]
		var next []chan []byte
		for _, c := range subs {
			if c != ch {
				next = append(next, c)
			}
		}
		if len(next) == 0 {
			delete(h.httpScreenSubscribers, studentID)
		} else {
			h.httpScreenSubscribers[studentID] = next
		}
	} else if kind == "webcam" {
		subs := h.httpWebcamSubscribers[studentID]
		var next []chan []byte
		for _, c := range subs {
			if c != ch {
				next = append(next, c)
			}
		}
		if len(next) == 0 {
			delete(h.httpWebcamSubscribers, studentID)
		} else {
			h.httpWebcamSubscribers[studentID] = next
		}
	}

	// If no subscribers left (WS or HTTP), stop student's stream
	wsSubs := h.subscribers[studentID]
	httpScSubs := h.httpScreenSubscribers[studentID]
	httpCamSubs := h.httpWebcamSubscribers[studentID]

	if len(wsSubs) == 0 && len(httpScSubs) == 0 && len(httpCamSubs) == 0 {
		if student, exists := h.students[studentID]; exists {
			_ = student.WriteJSON(SocketMsg{Event: "stop_screenshot_stream"})
			_ = student.WriteJSON(SocketMsg{Event: "stop_webcam_stream"})
		}
	}

	close(ch)
}

func GetStudentScreenStreamHandler(c *fiber.Ctx) error {
	studentIDVal := c.Params("studentId")
	studentID, err := strconv.ParseInt(studentIDVal, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).SendString("Invalid student ID")
	}

	ch := Hub.RegisterHttpSubscriber(studentID, "screen")
	c.Set("Content-Type", "multipart/x-mixed-replace; boundary=frame")
	c.Set("Cache-Control", "no-cache")
	c.Set("Connection", "keep-alive")
	c.Set("Pragma", "no-cache")

	c.Status(fiber.StatusOK)
	c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
		defer Hub.UnregisterHttpSubscriber(studentID, "screen", ch)

		for frame := range ch {
			_, _ = fmt.Fprintf(w, "--frame\r\n")
			_, _ = fmt.Fprintf(w, "Content-Type: image/jpeg\r\n")
			_, _ = fmt.Fprintf(w, "Content-Length: %d\r\n\r\n", len(frame))
			_, _ = w.Write(frame)
			_, _ = fmt.Fprintf(w, "\r\n")
			if err := w.Flush(); err != nil {
				return
			}
		}
	})

	return nil
}

func GetStudentWebcamStreamHandler(c *fiber.Ctx) error {
	studentIDVal := c.Params("studentId")
	studentID, err := strconv.ParseInt(studentIDVal, 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).SendString("Invalid student ID")
	}

	ch := Hub.RegisterHttpSubscriber(studentID, "webcam")
	c.Set("Content-Type", "multipart/x-mixed-replace; boundary=frame")
	c.Set("Cache-Control", "no-cache")
	c.Set("Connection", "keep-alive")
	c.Set("Pragma", "no-cache")

	c.Status(fiber.StatusOK)
	c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
		defer Hub.UnregisterHttpSubscriber(studentID, "webcam", ch)

		for frame := range ch {
			_, _ = fmt.Fprintf(w, "--frame\r\n")
			_, _ = fmt.Fprintf(w, "Content-Type: image/jpeg\r\n")
			_, _ = fmt.Fprintf(w, "Content-Length: %d\r\n\r\n", len(frame))
			_, _ = w.Write(frame)
			_, _ = fmt.Fprintf(w, "\r\n")
			if err := w.Flush(); err != nil {
				return
			}
		}
	})

	return nil
}
