package protocol

import (
	"encoding/json"
	"fmt"
	"time"
)

type PacketType int

const (
	// Client requests user certificate
	FlagGetUserCert PacketType = iota

	// Client requests unread message info
	FlagGetUnreadMsgsInfo

	// Client requests a message from the queue
	FlagGetMsg

	// Client sends a message
	FlagSendMsg

	// Server sends user certificate
	FlagAnswerGetUserCert

	// Server sends list of unread messages
	FlagAnswerGetUnreadMsgsInfo

	// Server sends requested message
	FlagAnswerGetMsg

    // Server tells the client that the message was successfully sent
    FlagAnswerSendMsg

	// Report an error
	FlagReportError
)

type (
	GetUserCert struct {
		UID string `json:"uid"`
	}

	GetUnreadMsgsInfo struct {
		Page     int `json:"page"`
		PageSize int `json:"pageSize"`
	}

	GetMsg struct {
		Num int `json:"num"`
	}

	SendMsg struct {
		ToUID   string `json:"to_uid"`
		Subject []byte `json:"subject"`
		Body    []byte `json:"body"`
	}

	AnswerGetUserCert struct {
		UID         string `json:"uid"`
		Certificate []byte `json:"certificate"`
	}

	AnswerGetUnreadMsgsInfo struct {
		Page         int       `json:"page"`
		NumPages     int       `json:"num_pages"`
		MessagesInfo []MsgInfo `json:"messages_info"`
	}

	MsgInfo struct {
		Num       int       `json:"num"`
		FromUID   string    `json:"from_uid"`
		Subject   []byte    `json:"subject"`
		Timestamp time.Time `json:"timestamp"`
	}

	AnswerGetMsg struct {
		FromUID   string    `json:"from_uid"`
		ToUID     string    `json:"to_uid"`
		Subject   []byte    `json:"subject"`
		Body      []byte    `json:"body"`
		Timestamp time.Time `json:"timestamp"`
	}

	ReportError struct {
		ErrorMessage string `json:"error"`
	}
)

type PacketBody interface{}

type Packet struct {
	Flag PacketType `json:"flag"`
	Body PacketBody `json:"body"`
}

func NewPacket(fl PacketType, body PacketBody) Packet {
	return Packet{
		Flag: fl,
		Body: body,
	}

}

func NewGetUserCert(UID string) GetUserCert {
	return GetUserCert{
		UID: UID,
	}
}

func NewGetUnreadMsgsInfo(page int, pageSize int) GetUnreadMsgsInfo {
	return GetUnreadMsgsInfo{
		Page:     page,
		PageSize: pageSize}
}

func NewGetMsg(num int) GetMsg {
	return GetMsg{
		Num: num,
	}
}

func NewSendMsg(toUID string, subject []byte, body []byte) SendMsg {
	return SendMsg{
		ToUID:   toUID,
		Subject: subject,
		Body:    body,
	}
}

func NewAnswerGetUserCert(uid string, certificate []byte) AnswerGetUserCert {
	return AnswerGetUserCert{
		UID:         uid,
		Certificate: certificate,
	}
}

func NewAnswerGetUnreadMsgsInfo(page int, numPages int, messagesInfo []MsgInfo) AnswerGetUnreadMsgsInfo {
	return AnswerGetUnreadMsgsInfo{Page: page, NumPages: numPages, MessagesInfo: messagesInfo}
}
func NewMsgInfo(num int, fromUID string, subject []byte, timestamp time.Time) MsgInfo {
	return MsgInfo{
		Num:       num,
		FromUID:   fromUID,
		Subject:   subject,
		Timestamp: timestamp,
	}
}

func NewAnswerGetMsg(fromUID, toUID string, subject []byte, body []byte, timestamp time.Time, last bool) AnswerGetMsg {
	return AnswerGetMsg{
		FromUID:   fromUID,
		ToUID:     toUID,
		Subject:   subject,
		Body:      body,
		Timestamp: timestamp,
	}
}

func NewReportError(errorMessage string) ReportError {
	return ReportError{
		ErrorMessage: errorMessage,
	}
}

func NewGetUserCertPacket(UID string) Packet {
	return NewPacket(FlagGetUserCert, NewGetUserCert(UID))
}

func NewGetUnreadMsgsInfoPacket(page int, pageSize int) Packet {
	return NewPacket(FlagGetUnreadMsgsInfo, NewGetUnreadMsgsInfo(page, pageSize))
}

func NewGetMsgPacket(num int) Packet {
	return NewPacket(FlagGetMsg, NewGetMsg(num))
}

func NewSendMsgPacket(toUID string, subject []byte, body []byte) Packet {
	return NewPacket(FlagSendMsg, NewSendMsg(toUID, subject, body))
}

func NewAnswerGetUserCertPacket(uid string, certificate []byte) Packet {
	return NewPacket(FlagAnswerGetUserCert, NewAnswerGetUserCert(uid, certificate))
}

func NewAnswerGetUnreadMsgsInfoPacket(page int, numPages int, messagesInfo []MsgInfo) Packet {
	return NewPacket(FlagAnswerGetUnreadMsgsInfo, NewAnswerGetUnreadMsgsInfo(page, numPages, messagesInfo))
}

func NewAnswerGetMsgPacket(fromUID, toUID string, subject []byte, body []byte, timestamp time.Time, last bool) Packet {
	return NewPacket(FlagAnswerGetMsg, NewAnswerGetMsg(fromUID, toUID, subject, body, timestamp, last))
}

func NewAnswerSendMsgPacket() Packet{
    //This packet has no body
    return NewPacket(FlagAnswerSendMsg,nil)
}

func NewReportErrorPacket(errorMessage string) Packet {
	return NewPacket(FlagReportError, NewReportError(errorMessage))
}

func UnmarshalGetUserCert(data PacketBody) GetUserCert {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet GetUserCert
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into GetUserCert: %v", err))
	}
	return packet
}

func UnmarshalGetUnreadMsgsInfo(data PacketBody) GetUnreadMsgsInfo {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet GetUnreadMsgsInfo
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into GetUnreadMsgsInfo: %v", err))
	}
	return packet
}

func UnmarshalGetMsg(data PacketBody) GetMsg {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet GetMsg
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into GetMsg: %v", err))
	}
	return packet
}

func UnmarshalSendMsg(data PacketBody) SendMsg {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet SendMsg
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into SendMsg: %v", err))
	}
	return packet
}

func UnmarshalAnswerGetUserCert(data PacketBody) AnswerGetUserCert {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet AnswerGetUserCert
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into AnswerGetUserCert: %v", err))
	}
	return packet
}
func UnmarshalUnreadMsgInfo(data PacketBody) MsgInfo {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet MsgInfo
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into UnreadMsgInfo: %v", err))
	}
	return packet
}

func UnmarshalAnswerGetUnreadMsgsInfo(data PacketBody) AnswerGetUnreadMsgsInfo {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet AnswerGetUnreadMsgsInfo
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into AnswerGetUnreadMsgsInfo: %v", err))
	}
	return packet
}

func UnmarshalAnswerGetMsg(data PacketBody) AnswerGetMsg {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet AnswerGetMsg
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into AnswerGetMsg: %v", err))
	}
	return packet
}

func UnmarshalReportError(data PacketBody) ReportError {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(fmt.Errorf("failed to marshal data: %v", err))
	}
	var packet ReportError
	if err := json.Unmarshal(jsonData, &packet); err != nil {
		panic(fmt.Errorf("failed to unmarshal into AnswerGetMsg: %v", err))
	}
	return packet
}