package handlers

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"mc-monitor/models"
	"net/http"
	"net/url"
	"os"
	"time"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type ServerUpdate struct {
	Servers []models.ServerInfo `json:"servers"`
	TS      int64               `json:"ts"`
}

func WebsocketHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}
	defer conn.Close()

	closeCh := make(chan struct{})
	go func() {
		defer close(closeCh)
		for {
			if _, _, err := conn.ReadMessage(); err != nil {
				return
			}
		}
	}()

	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case t := <-ticker.C:
			update := ServerUpdate{
				Servers: models.UpdateServers(),
				TS:      t.Unix(),
			}
			payload, _ := json.Marshal(update)
			if err := conn.WriteMessage(websocket.TextMessage, payload); err != nil {
				return
			}
			go writeTelemetry(update.Servers, t)
		case <-closeCh:
			log.Println("ws closed")
			return
		}
	}
}

func writeTelemetry(servers []models.ServerInfo, ts time.Time) {
	baseURL := os.Getenv("CLICKHOUSE_URL")
	if baseURL == "" {
		baseURL = "http://clickhouse.vsosh:8123"
	}

	query := "INSERT INTO monitor.telemetry FORMAT JSONEachRow"
	target := fmt.Sprintf("%s/?query=%s", baseURL, url.QueryEscape(query))

	var buf bytes.Buffer
	tsText := ts.UTC().Format("2006-01-02 15:04:05")
	for _, srv := range servers {
		line := fmt.Sprintf("{\"ts\":\"%s\",\"server\":\"%s\",\"region\":\"%s\",\"online\":%d,\"tps\":%.2f,\"mspt\":%.2f}\n",
			tsText,
			escapeJSON(srv.ID),
			escapeJSON(srv.Region),
			srv.Online,
			srv.TPS,
			srv.MSPT,
		)
		buf.WriteString(line)
	}

	req, err := http.NewRequest(http.MethodPost, target, &buf)
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 2 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return
	}
	resp.Body.Close()
}

func escapeJSON(value string) string {
	encoded, _ := json.Marshal(value)
	if len(encoded) >= 2 {
		return string(encoded[1 : len(encoded)-1])
	}
	return value
}
