package notification

// Connection defines the interface needed for writing messages to clients.
// This decouples our Hub from the gorilla/websocket package, allowing easy testing.
type Connection interface {
	WriteMessage(message []byte) error
	Close() error
}

// Client represents a connected user subscription.
type Client struct {
	Hub  *Hub
	Conn Connection
	Send chan []byte
}

// Hub maintains the set of active clients and broadcasts messages to them.
type Hub struct {
	// Registered clients.
	Clients map[*Client]bool

	// Inbound messages from the clients / Redis.
	Broadcast chan []byte

	// Register requests from the clients.
	Register chan *Client

	// Unregister requests from clients.
	Unregister chan *Client
}

// NewHub initializes a new Hub.
func NewHub() *Hub {
	return &Hub{
		Broadcast:  make(chan []byte),
		Register:   make(chan *Client),
		Unregister: make(chan *Client),
		Clients:    make(map[*Client]bool),
	}
}

// Run executes the main event loop for client registration and broadcasting.
// This function runs forever, processing messages from the channels using select.
func (h *Hub) Run() {
	for {
		select {
		case client := <-h.Register:
			h.Clients[client] = true
		case client := <-h.Unregister:
			if _, ok := h.Clients[client]; ok {
				delete(h.Clients, client)
				close(client.Send)
			}
		case msg := <-h.Broadcast:
			for client := range h.Clients {
				select {
				case client.Send <- msg:
				default:
					close(client.Send)
					delete(h.Clients, client)
				}
			}
		}
	}
	// TODO: Implement the select-based event loop.
	// Hint: Loop forever using `for {}`.
	// Inside the loop, select on:
	// 1. <-h.Register: add client to h.Clients map.
	// 2. <-h.Unregister: check if client exists in h.Clients, delete it, and close client.Send.
	// 3. msg := <-h.Broadcast: iterate over h.Clients, send msg to client.Send.
	//    Hint: handle non-blocking sends using select:
	//    select {
	//    case client.Send <- msg:
	//    default:
	//        close(client.Send)
	//        delete(h.Clients, client)
	//    }
}

// WritePump pumps messages from the Hub to the WebSocket connection.
// A goroutine running WritePump is started for each client connection.
func (c *Client) WritePump() {
	defer c.Conn.Close()
	for msg := range c.Send {
		err := c.Conn.WriteMessage(msg)
		if err != nil {
			break
		}
	}
	// TODO: Loop over c.Send channel.
	// Hint: `for msg := range c.Send { ... }`
	// Inside the loop, call c.Conn.WriteMessage(msg).
	// If writing fails, break the loop.
	// Ensure the connection is closed when the pump exits (use defer c.Conn.Close()).
}
