package structs

import (
	"encoding/json"
	"errors"
	"sync"
	"time"

	"github.com/gofiber/websocket/v2"
	"github.com/rs/zerolog/log"
)

const (
	// Note: Status codes in the range 4000-4999 are reserved for private use
	SocketCloseCodeUnauthorized  = 4001
	SocketCloseCodeSessionClosed = 4002 // when user logs out of the session and has multiple tabs with the same session open
)

type SocketClient struct {
	SessionId         string
	BrowserTabSession string
	OrganizationId    string
	UserId            string
	Conn              *websocket.Conn
	connMu            sync.Mutex
	SubscribedTopic   string
}

type SocketMessage struct {
	Conn *websocket.Conn
	Msg  []byte
}

type SendSocketMessage struct {
	Cmd  int
	Body any
}

type ReceivedMessage struct {
	Cmd  int
	Body map[string]interface{}
}

func (socketClient *SocketClient) SendSessionClosedMessage() error {
	return socketClient.writeMessage(websocket.CloseMessage, SendSocketMessage{}, SocketCloseCodeSessionClosed)
}

func (socketClient *SocketClient) SendUnauthorizedCloseMessage() error {
	return socketClient.writeMessage(websocket.CloseMessage, SendSocketMessage{}, SocketCloseCodeUnauthorized)
}

func (socketClient *SocketClient) SendMessage(message SendSocketMessage) error {
	return socketClient.writeMessage(websocket.TextMessage, message, 0)
}

func (socketClient *SocketClient) writeMessage(messageType int, message SendSocketMessage, closeMessageCode int) error {
	var marshaledMessage []byte
	var err error

	if closeMessageCode > 0 {
		marshaledMessage = websocket.FormatCloseMessage(closeMessageCode, "")
	} else {
		marshaledMessage, err = json.Marshal(message)

		if err != nil {
			log.Error().Msgf("Failed to marshal ws message, err: %s", err)
			return err
		}
	}

	socketClient.connMu.Lock()
	defer socketClient.connMu.Unlock()

	if socketClient.Conn == nil {
		log.Error().Msgf("Failed to ws message because conn is nil")
		return errors.New("ws client conn is nil")
	}

	err = socketClient.Conn.WriteMessage(messageType, marshaledMessage)

	if err != nil {
		log.Error().Msgf("Failed to write ws message, err: %s", err)
		return err
	}

	return nil
}

type AllUsers struct {
	Id               string
	RoleId           string
	Avatar           string
	Username         string
	ConnectionStatus uint8
	Deactivated      bool
	LastOnline       time.Time
}

type UserSessionSocket struct {
	IdForDeletion    string
	UserAgent        string
	ConnectionStatus uint8
	LastUsed         time.Time
	ExpiresAt        time.Time
}
