package channelserver

import (
	"time"

	"erupe-ce/common/byteframe"
	ps "erupe-ce/common/pascalstring"
	"erupe-ce/common/stringsupport"
	"erupe-ce/network/mhfpacket"
	"go.uber.org/zap"
)

// MessageBoardPost represents a guild message board post.
type MessageBoardPost struct {
	ID        uint32    `db:"id"`
	StampID   uint32    `db:"stamp_id"`
	Title     string    `db:"title"`
	Body      string    `db:"body"`
	AuthorID  uint32    `db:"author_id"`
	Timestamp time.Time `db:"created_at"`
	LikedBy   string    `db:"liked_by"`
}

func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
	pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMessageBoard)
	guild, err := s.server.guildRepo.GetByCharID(s.charID)
	if err != nil {
		s.logger.Error("Failed to get guild for message board", zap.Error(err))
		doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
		return
	}
	if pkt.BoardType == 1 {
		pkt.MaxPosts = 4
	}
	posts, err := s.server.guildRepo.ListPosts(guild.ID, int(pkt.BoardType))
	if err != nil {
		s.logger.Error("Failed to get guild messages from db", zap.Error(err))
		doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
		return
	}
	if err := s.server.charRepo.UpdateGuildPostChecked(s.charID); err != nil {
		s.logger.Error("Failed to update guild post checked time", zap.Error(err))
	}
	bf := byteframe.NewByteFrame()
	for _, postData := range posts {
		bf.WriteUint32(postData.ID)
		bf.WriteUint32(postData.AuthorID)
		bf.WriteUint32(0)
		bf.WriteUint32(uint32(postData.Timestamp.Unix()))
		bf.WriteUint32(uint32(stringsupport.CSVLength(postData.LikedBy)))
		bf.WriteBool(stringsupport.CSVContains(postData.LikedBy, int(s.charID)))
		bf.WriteUint32(postData.StampID)
		ps.Uint32(bf, postData.Title, true)
		ps.Uint32(bf, postData.Body, true)
	}
	data := byteframe.NewByteFrame()
	data.WriteUint32(uint32(len(posts)))
	data.WriteBytes(bf.Data())
	doAckBufSucceed(s, pkt.AckHandle, data.Data())
}

func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
	pkt := p.(*mhfpacket.MsgMhfUpdateGuildMessageBoard)
	guild, err := s.server.guildRepo.GetByCharID(s.charID)
	applicant := false
	if guild != nil {
		var appErr error
		applicant, appErr = s.server.guildRepo.HasApplication(guild.ID, s.charID)
		if appErr != nil {
			s.logger.Warn("Failed to check guild application status", zap.Error(appErr))
		}
	}
	if err != nil || guild == nil || applicant {
		doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
		return
	}
	switch pkt.MessageOp {
	case 0: // Create message
		maxPosts := 100
		if pkt.PostType == 1 {
			maxPosts = 4
		}
		if err := s.server.guildRepo.CreatePost(guild.ID, s.charID, pkt.StampID, int(pkt.PostType), pkt.Title, pkt.Body, maxPosts); err != nil {
			s.logger.Error("Failed to create guild post", zap.Error(err))
		}
	case 1: // Delete message
		if err := s.server.guildRepo.DeletePost(pkt.PostID); err != nil {
			s.logger.Error("Failed to soft-delete guild post", zap.Error(err))
		}
	case 2: // Update message
		if err := s.server.guildRepo.UpdatePost(pkt.PostID, pkt.Title, pkt.Body); err != nil {
			s.logger.Error("Failed to update guild post", zap.Error(err))
		}
	case 3: // Update stamp
		if err := s.server.guildRepo.UpdatePostStamp(pkt.PostID, pkt.StampID); err != nil {
			s.logger.Error("Failed to update guild post stamp", zap.Error(err))
		}
	case 4: // Like message
		likedBy, err := s.server.guildRepo.GetPostLikedBy(pkt.PostID)
		if err != nil {
			s.logger.Error("Failed to get guild message like data from db", zap.Error(err))
		} else {
			if pkt.LikeState {
				likedBy = stringsupport.CSVAdd(likedBy, int(s.charID))
			} else {
				likedBy = stringsupport.CSVRemove(likedBy, int(s.charID))
			}
			if err := s.server.guildRepo.SetPostLikedBy(pkt.PostID, likedBy); err != nil {
				s.logger.Error("Failed to update guild post likes", zap.Error(err))
			}
		}
	case 5: // Check for new messages
		timeChecked, err := s.server.charRepo.ReadGuildPostChecked(s.charID)
		if err == nil {
			newPosts, countErr := s.server.guildRepo.CountNewPosts(guild.ID, timeChecked)
			if countErr != nil {
				s.logger.Warn("Failed to count new guild posts", zap.Error(countErr))
			}
			if newPosts > 0 {
				doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
				return
			}
		}
	}
	doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
