package main

import (
	"crypto/sha256"
	"fmt"
	"io"
	"log"
	"net/http"
	"slices"
	"strconv"
	"strings"

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

type Message struct {
	Event string `json:"event"`
	Data  string `json:"data"`
	Id    string `json:"id"`
}

type ClientChan chan Message

type Client struct {
	Id     string     `json:"id"`
	Ip     string     `json:"ip"`
	Chan   ClientChan `json:"chan"`
	Events []string   `json:"events"`
}

type Event struct {
	Clients []Client `json:"clients"`
	Id      int      `json:"id"`
	Events  []string `json:"events"`
}

func main() {

	r := gin.Default()
	r.Use(cors.Default())

	event := NewServer()

	api := r.Group("/api")
	// api.GET("/sse", HeadersMiddleware(), event.serveHTTP(), event.retvalSSE())
	api.GET("/sse", HeadersMiddleware(), event.Stream)
	api.GET("/msg", event.SendMsg)
	api.GET("/message", event.SendMessage)
	api.GET("/ping", event.SendPing)
	api.GET("/pong", event.SendPong)

	sse := r.Group("/sse")
	sse.Static("/", "./static/sse")

	app := r.Group("/_app")
	app.Static("/", "./static/_app")

	static := r.Group("/static")
	static.Static("/", "./static/static")

	r.StaticFile("/", "./static/index.html")
	r.StaticFile("/favicon.png", "./static/favicon.png")

	r.ForwardedByClientIP = true
	r.SetTrustedProxies([]string{"127.0.0.1"})

	r.Run(":8888")
}

func NewServer() *Event {
	return &Event{
		Clients: []Client{},
		Id:      0,
		Events: []string{
			"ping",
			"pong",
			"msg",
			"message",
		},
	}
}

func HeadersMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		headers := c.Writer.Header()
		headers.Set("Content-Type", "text/event-stream")
		headers.Set("Cache-Control", "no-cache")
		headers.Set("Connection", "keep-alive")
		headers.Set("Transfer-Encoding", "chunked")
		c.Next()
	}
}

func (e *Event) Stream(c *gin.Context) {
	client := &Client{
		Ip:     c.Request.RemoteAddr,
		Chan:   make(ClientChan),
		Events: c.QueryArray("event"),
	}

	client.Id = Id(client.Ip, client.Events, 8)

	log.Printf("Client: %+v", client)

	e.Clients = append(e.Clients, *client)

	defer func() {
		e.Clients = remove(e.Clients, client)
		close(client.Chan)
		log.Printf("Client %s disconnected - tot: %d", client.Ip, len(e.Clients))
	}()

	log.Printf("Client %s connected - tot: %d", client.Ip, len(e.Clients))

	c.Stream(func(w io.Writer) bool {
		msg, ok := <-client.Chan
		if ok {
			c.SSEvent(msg.Event, msg)
		}
		return ok
	})
}

func (e *Event) Send(msg Message) {
	e.Id++
	msg.Id = strconv.Itoa(e.Id)
	for _, client := range e.Clients {
		if slices.Contains(client.Events, msg.Event) || slices.Contains(client.Events, "*") {
			client.Chan <- msg
		}
	}
}

func (e *Event) SendMessage(c *gin.Context) {
	e.Send(Message{
		Event: "message",
		Data:  "Ciao",
	})
	e.Send(Message{
		Data: "Senza event",
	})
}

func (e *Event) SendMsg(c *gin.Context) {
	e.Send(Message{
		Event: "msg",
		Data:  "hello",
	})
	c.JSON(http.StatusOK, gin.H{"status": "msg"})
}

func (e *Event) SendPing(c *gin.Context) {
	e.Send(Message{
		Event: "ping",
		Data:  "Ping",
	})
	c.JSON(http.StatusOK, gin.H{"status": "ping"})
}

func (e *Event) SendPong(c *gin.Context) {
	e.Send(Message{
		Event: "pong",
		Data:  "Pong",
	})
	c.JSON(http.StatusOK, gin.H{"status": "pong"})
}

func remove(slice []Client, s *Client) []Client {
	for i, v := range slice {
		if v.Id == s.Id {
			return append(slice[:i], slice[i+1:]...)
		}
	}
	return slice
}

func Id(ip string, events []string, len int) string {
	str := ip + strings.Join(events, "|")
	id := sha256.Sum256([]byte(str))

	return fmt.Sprintf("%X", id)[0:len]
}

// func (e *Event) serveHTTP() gin.HandlerFunc {
// 	return func(c *gin.Context) {
// 		// Initialize client channel
// 		clientChan := make(ClientChan)

// 		// Send new connection to event server
// 		e.NewClient <- clientChan

// 		defer func() {
// 			// Send closed connection to event server
// 			e.ClosedClient <- clientChan
// 		}()

// 		c.Set("clientChan", clientChan)

// 		c.Next()
// 	}
// }

// func (e *Event) retvalSSE() gin.HandlerFunc {
// 	return func(c *gin.Context) {
// 		v, ok := c.Get("clientChan")
// 		if !ok {
// 			return
// 		}

// 		clientChan, ok := v.(ClientChan)
// 		if !ok {
// 			return
// 		}

// 		c.Stream(func(w io.Writer) bool {
// 			msg, ok := <-clientChan
// 			if ok {
// 				c.SSEvent("message", msg)
// 			}
// 			return ok
// 		})
// 	}
// }

// func (e *Event) Broadcast() {
// 	id := 0
// 	for {
// 		select {
// 		case client := <-e.NewClient:
// 			e.TotalClients[client] = true
// 		case client := <-e.ClosedClient:
// 			delete(e.TotalClients, client)
// 			close(client)
// 		case message := <-e.Message:
// 			id++
// 			for client := range e.TotalClients {
// 				// msg, _ := json.Marshal(message)
// 				// client <- string(msg)
// 				client <- message
// 			}
// 		}
// 	}
// }
