// cmd/server/main.go
package main

import (
	"context"
	"embed"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/fs"
	"log"
	"net/http"
	"os"
	"time"

	"gpio-monitor/internal/adapter"
)

// ===== EMBED WEB =====
//
//go:embed web/*
var webFiles embed.FS

type API struct {
	buf       *adapter.RingBuffer
	startTime time.Time
}

func writeJSON(w http.ResponseWriter, v any) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(v)
}

func (a *API) HandleHealth(w http.ResponseWriter, r *http.Request) {
	latest, ok := a.buf.GetLatest()
	idle := time.Since(a.buf.LastWriteTime())

	status := "На связи"
	if idle > 15*time.Second {
		status = "Нет связи"
	}

	writeJSON(w, map[string]any{
		"status":       status,
		"uptime_sec":   time.Since(a.startTime).Seconds(),
		"last_data_ms": idle.Milliseconds(),
		"pipe_alive":   a.buf.IsAlive(3 * time.Second),
		"has_data":     ok,
		"latest":       latest,
		"stats":        a.buf.Stats(),
	})
}

func (a *API) HandleLatest(w http.ResponseWriter, r *http.Request) {
	latest, ok := a.buf.GetLatest()
	if !ok {
		writeJSON(w, map[string]string{"error": "no data"})
		return
	}

	writeJSON(w, map[string]any{
		"latest":  latest,
		"history": a.buf.GetLast(10),
	})
}

func (a *API) HandleHistory(w http.ResponseWriter, r *http.Request) {
	raw := a.buf.GetLast(100)

	out := make([]int, len(raw))
	for i, v := range raw {
		out[i] = int(v)
	}

	writeJSON(w, map[string]any{
		"bytes": out,
	})
}

func (a *API) HandleStream(w http.ResponseWriter, r *http.Request) {
	// Проверяем доступность go2rtc
	resp, err := http.Get("http://localhost:1984/api/streams?src=cam")
	camAvailable := err == nil && resp.StatusCode == 200
	if resp != nil {
		resp.Body.Close()
	}

	writeJSON(w, map[string]interface{}{
		"cam":       "/api/cam",
		"available": camAvailable,
		"source":    fmt.Sprintf("http://%s:1984/api/stream.mjpeg?src=cam", r.Host),
	})
}

// Прокси для MJPEG потока с go2rtc
func handleCamProxy(w http.ResponseWriter, r *http.Request) {
	log.Printf("[cam] proxy request from %s", r.RemoteAddr)

	resp, err := http.Get(
		"http://127.0.0.1:1984/api/stream.mjpeg?src=cam",
	)
	if err != nil {
		log.Printf("[cam] go2rtc unavailable: %v", err)
		http.Error(w, "camera unavailable", 503)
		return
	}
	defer resp.Body.Close()

	for k, vv := range resp.Header {
		for _, v := range vv {
			w.Header().Add(k, v)
		}
	}

	w.Header().Set("Access-Control-Allow-Origin", "*")

	w.WriteHeader(resp.StatusCode)

	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "stream unsupported", 500)
		return
	}

	buf := make([]byte, 32*1024)

	for {
		n, err := resp.Body.Read(buf)

		if n > 0 {
			_, err = w.Write(buf[:n])
			if err != nil {
				log.Printf("[cam] client disconnected")
				return
			}

			flusher.Flush()
		}

		if err != nil {
			if err != io.EOF {
				log.Printf("[cam] stream ended: %v", err)
			}
			return
		}
	}
}

// ===== PIPE READER =====
func startPipeReader(ctx context.Context, buf *adapter.RingBuffer, path string) {
	go func() {
		buffer := make([]byte, 64)

		for {
			select {
			case <-ctx.Done():
				return
			default:
			}

			f, err := os.OpenFile(path, os.O_RDONLY, 0)
			if err != nil {
				time.Sleep(time.Second)
				continue
			}

			log.Println("pipe connected")

			for {
				n, err := f.Read(buffer)
				if err != nil {
					f.Close()
					break
				}

				for i := 0; i < n; i++ {
					buf.Write(buffer[i])
				}
			}

			log.Println("pipe disconnected, retry...")
		}
	}()
}

func cors(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

		if r.Method == "OPTIONS" {
			w.WriteHeader(200)
			return
		}

		next(w, r)
	}
}

func main() {
	pipePath := flag.String("pipe", "/tmp/gpio_pipe", "pipe path")
	flag.Parse()

	buf := adapter.NewRingBuffer(1024 * 10)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	startPipeReader(ctx, buf, *pipePath)

	api := &API{
		buf:       buf,
		startTime: time.Now(),
	}

	// ===== API =====
	http.Handle("/api/", http.StripPrefix("/api", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/latest":
			cors(api.HandleLatest)(w, r)
		case "/history":
			cors(api.HandleHistory)(w, r)
		case "/health":
			cors(api.HandleHealth)(w, r)
		case "/stream":
			cors(api.HandleStream)(w, r)
		case "/cam":
			handleCamProxy(w, r)
		default:
			http.Error(w, "not found", 404)
		}
	})))

	// ===== WEB (EMBEDDED) =====
	webFS, err := fs.Sub(webFiles, "web")
	if err != nil {
		log.Fatal(err)
	}

	http.Handle("/", http.FileServer(http.FS(webFS)))

	log.Println("Server started on :8080")
	log.Println("Camera proxy available at /api/cam")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
