package service

import (
	"context"
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/FlipAppDev/FlipServer/server/internal/domain/dto"
	"github.com/FlipAppDev/FlipServer/server/internal/domain/model"
	"github.com/FlipAppDev/FlipServer/server/internal/domain/types"
	"github.com/pkg/errors"
	"google.golang.org/protobuf/proto"
)

type buildMessageParam struct {
	User           model.User
	ReceiveUser    model.User
	Room           model.Room
	Text           string
	IsPublic       bool
	Game           model.Game
	DrawGuessParam drawGuessParam
}

type drawGuessParam struct {
	TwoPeopleDrawGameType types.TwoPeopleDrawGameType
	GameDTO               dto.GameDTO
}

type buildMessageResult struct {
	Message     model.Message
	RichTextDTO dto.RichTextDTO
}

type MessageBuilder struct {
}

func (s MessageBuilder) buildForSendMessageInRoom(ctx context.Context, b buildMessageParam) (buildMessageResult, error) {
	richTextDTO, err := s.buildRichTextForSendMessageInRoom(ctx, b)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "build richText")
	}

	richText, err := richTextBuilder{}.toEntity(richTextDTO)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "convert richText")
	}

	body, err := proto.Marshal(richText)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "marshal richText")
	}

	tmp := model.Message{
		ID:         types.NewMessageID(),
		MsgType:    types.MsgTypeRoom,
		SenderID:   s.newSenderIDForUser(b.User),
		ReceiverID: s.newReceiverIDForRoom(b.Room),
		SendTime:   time.Now().UnixNano() / int64(time.Millisecond),
		Public:     b.IsPublic,
		Sender: model.MessageSender{
			ID:       b.User.ID,
			Nickname: b.User.Nickname,
			Avatar:   b.User.Avatar,
			UUID:     b.User.UUID.String(),
		},
		Receiver: model.MessageReceiver{},
		BodyType: int(types.MsgBodyTypeText),
		Body:     body,
	}
	m, err := model.NewMessage(tmp)
	if err != nil {
		return buildMessageResult{}, err
	}

	return buildMessageResult{
		Message:     m,
		RichTextDTO: richTextDTO,
	}, nil
}

func (s MessageBuilder) buildRichTextForSendMessageInRoom(ctx context.Context, b buildMessageParam) (dto.RichTextDTO, error) {
	textEntryDTO := dto.TextEntryDTO{
		Text: b.Text,
	}

	richTextDTO := dto.RichTextDTO{
		TextEntryDTOs: []dto.TextEntryDTO{
			textEntryDTO,
		},
	}

	return richTextDTO, nil
}

func (s MessageBuilder) buildForSendMessageForReceiverInRoom(ctx context.Context, b buildMessageParam) (buildMessageResult, error) {
	richTextDTO, err := s.buildRichTextForSendMessageForReceiverInRoom(ctx, b)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "build richText")
	}

	richText, err := richTextBuilder{}.toEntity(richTextDTO)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "convert richText")
	}

	body, err := proto.Marshal(richText)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "marshal richText")
	}

	tmp := model.Message{
		ID:         types.NewMessageID(),
		MsgType:    types.MsgTypeRoom,
		SenderID:   s.newSenderIDForUser(b.User),
		ReceiverID: s.newReceiverIDForRoom(b.Room),
		SendTime:   time.Now().UnixNano() / int64(time.Millisecond),
		Public:     b.IsPublic,
		Sender: model.MessageSender{
			ID:       b.User.ID,
			Nickname: b.User.Nickname,
			Avatar:   b.User.Avatar,
			UUID:     b.User.UUID.String(),
		},
		Receiver: model.MessageReceiver{
			ID:       b.ReceiveUser.ID,
			Nickname: b.ReceiveUser.Nickname,
			Avatar:   b.ReceiveUser.Avatar,
		},
		BodyType: int(types.MsgBodyTypeText),
		Body:     body,
	}
	m, err := model.NewMessage(tmp)
	if err != nil {
		return buildMessageResult{}, err
	}

	return buildMessageResult{
		Message:     m,
		RichTextDTO: richTextDTO,
	}, nil
}

func (s MessageBuilder) buildRichTextForSendMessageForReceiverInRoom(ctx context.Context, b buildMessageParam) (dto.RichTextDTO, error) {
	text := b.Text

	atKeyword := fmt.Sprintf("@%s", b.ReceiveUser.Nickname)
	if !strings.Contains(b.Text, atKeyword) {
		richTextDTO := dto.RichTextDTO{
			TextEntryDTOs: []dto.TextEntryDTO{
				{
					Text: b.Text,
				},
			},
		}
		return richTextDTO, nil
	}

	textEntryDTOs := []dto.TextEntryDTO{}

	if !b.IsPublic {
		privateKey, err := s.findPrivateKeyFromText(text)
		if err != nil {
			return dto.RichTextDTO{}, err
		}
		if privateKey != "" {
			text = text[len(privateKey):]
			privateKeyTextEntryDTO := dto.TextEntryDTO{
				Text: privateKey,
				TextStyleDTO: dto.TextStyleDTO{
					Color: "#46D5B3",
				},
			}
			textEntryDTOs = append(textEntryDTOs, privateKeyTextEntryDTO)
		}
	}

	atUserTextEntryDTO := dto.TextEntryDTO{
		Text: atKeyword,
		TextStyleDTO: dto.TextStyleDTO{
			Color: "#3F9DF3",
		},
	}

	textParts := strings.Split(text, atKeyword)
	for i, textPart := range textParts {
		if i > 0 {
			textEntryDTOs = append(textEntryDTOs, atUserTextEntryDTO)
		}
		if textPart != "" {
			normalTextEntryDTO := dto.TextEntryDTO{
				Text: textPart,
			}
			textEntryDTOs = append(textEntryDTOs, normalTextEntryDTO)
		}
	}

	return dto.RichTextDTO{
		TextEntryDTOs: textEntryDTOs,
	}, nil
}

func (s MessageBuilder) findPrivateKeyFromText(text string) (string, error) {
	privateKeyRegex, err := regexp.Compile(`^(\[.+\])`)
	if err != nil {
		return "", errors.Wrap(err, "build privateKeyRegex")
	}

	ss := privateKeyRegex.FindStringSubmatch(text)
	if len(ss) <= 1 {
		return "", nil
	}
	return ss[1], nil
}

func (s MessageBuilder) newSenderIDForUser(user model.User) types.SenderID {
	return types.SenderID(fmt.Sprintf("%d", user.ID))
}

func (s MessageBuilder) newReceiverIDForRoom(room model.Room) types.ReceiverID {
	return types.ReceiverID(fmt.Sprintf("%s/%d", "ROOM", room.ID))
}
package service

import (
	"context"
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/FlipAppDev/FlipServer/server/internal/domain/dto"
	"github.com/FlipAppDev/FlipServer/server/internal/domain/model"
	"github.com/FlipAppDev/FlipServer/server/internal/domain/types"
	"github.com/pkg/errors"
	"google.golang.org/protobuf/proto"
)

type buildMessageParam struct {
	User           model.User
	ReceiveUser    model.User
	Room           model.Room
	Text           string
	IsPublic       bool
	Game           model.Game
	DrawGuessParam drawGuessParam
}

type drawGuessParam struct {
	TwoPeopleDrawGameType types.TwoPeopleDrawGameType
	GameDTO               dto.GameDTO
}

type buildMessageResult struct {
	Message     model.Message
	RichTextDTO dto.RichTextDTO
}

type MessageBuilder struct {
}

func (s MessageBuilder) buildForSendMessageInRoom(ctx context.Context, b buildMessageParam) (buildMessageResult, error) {
	richTextDTO, err := s.buildRichTextForSendMessageInRoom(ctx, b)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "build richText")
	}

	richText, err := richTextBuilder{}.toEntity(richTextDTO)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "convert richText")
	}

	body, err := proto.Marshal(richText)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "marshal richText")
	}

	tmp := model.Message{
		ID:         types.NewMessageID(),
		MsgType:    types.MsgTypeRoom,
		SenderID:   s.newSenderIDForUser(b.User),
		ReceiverID: s.newReceiverIDForRoom(b.Room),
		SendTime:   time.Now().UnixNano() / int64(time.Millisecond),
		Public:     b.IsPublic,
		Sender: model.MessageSender{
			ID:       b.User.ID,
			Nickname: b.User.Nickname,
			Avatar:   b.User.Avatar,
			UUID:     b.User.UUID.String(),
		},
		Receiver: model.MessageReceiver{},
		BodyType: int(types.MsgBodyTypeText),
		Body:     body,
	}
	m, err := model.NewMessage(tmp)
	if err != nil {
		return buildMessageResult{}, err
	}

	return buildMessageResult{
		Message:     m,
		RichTextDTO: richTextDTO,
	}, nil
}

func (s MessageBuilder) buildRichTextForSendMessageInRoom(ctx context.Context, b buildMessageParam) (dto.RichTextDTO, error) {
	textEntryDTO := dto.TextEntryDTO{
		Text: b.Text,
	}

	richTextDTO := dto.RichTextDTO{
		TextEntryDTOs: []dto.TextEntryDTO{
			textEntryDTO,
		},
	}

	return richTextDTO, nil
}

func (s MessageBuilder) buildForSendMessageForReceiverInRoom(ctx context.Context, b buildMessageParam) (buildMessageResult, error) {
	richTextDTO, err := s.buildRichTextForSendMessageForReceiverInRoom(ctx, b)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "build richText")
	}

	richText, err := richTextBuilder{}.toEntity(richTextDTO)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "convert richText")
	}

	body, err := proto.Marshal(richText)
	if err != nil {
		return buildMessageResult{}, errors.Wrap(err, "marshal richText")
	}

	tmp := model.Message{
		ID:         types.NewMessageID(),
		MsgType:    types.MsgTypeRoom,
		SenderID:   s.newSenderIDForUser(b.User),
		ReceiverID: s.newReceiverIDForRoom(b.Room),
		SendTime:   time.Now().UnixNano() / int64(time.Millisecond),
		Public:     b.IsPublic,
		Sender: model.MessageSender{
			ID:       b.User.ID,
			Nickname: b.User.Nickname,
			Avatar:   b.User.Avatar,
			UUID:     b.User.UUID.String(),
		},
		Receiver: model.MessageReceiver{
			ID:       b.ReceiveUser.ID,
			Nickname: b.ReceiveUser.Nickname,
			Avatar:   b.ReceiveUser.Avatar,
		},
		BodyType: int(types.MsgBodyTypeText),
		Body:     body,
	}
	m, err := model.NewMessage(tmp)
	if err != nil {
		return buildMessageResult{}, err
	}

	return buildMessageResult{
		Message:     m,
		RichTextDTO: richTextDTO,
	}, nil
}

func (s MessageBuilder) buildRichTextForSendMessageForReceiverInRoom(ctx context.Context, b buildMessageParam) (dto.RichTextDTO, error) {
	text := b.Text

	atKeyword := fmt.Sprintf("@%s", b.ReceiveUser.Nickname)
	if !strings.Contains(b.Text, atKeyword) {
		richTextDTO := dto.RichTextDTO{
			TextEntryDTOs: []dto.TextEntryDTO{
				{
					Text: b.Text,
				},
			},
		}
		return richTextDTO, nil
	}

	textEntryDTOs := []dto.TextEntryDTO{}

	if !b.IsPublic {
		privateKey, err := s.findPrivateKeyFromText(text)
		if err != nil {
			return dto.RichTextDTO{}, err
		}
		if privateKey != "" {
			text = text[len(privateKey):]
			privateKeyTextEntryDTO := dto.TextEntryDTO{
				Text: privateKey,
				TextStyleDTO: dto.TextStyleDTO{
					Color: "#46D5B3",
				},
			}
			textEntryDTOs = append(textEntryDTOs, privateKeyTextEntryDTO)
		}
	}

	atUserTextEntryDTO := dto.TextEntryDTO{
		Text: atKeyword,
		TextStyleDTO: dto.TextStyleDTO{
			Color: "#3F9DF3",
		},
	}

	textParts := strings.Split(text, atKeyword)
	for i, textPart := range textParts {
		if i > 0 {
			textEntryDTOs = append(textEntryDTOs, atUserTextEntryDTO)
		}
		if textPart != "" {
			normalTextEntryDTO := dto.TextEntryDTO{
				Text: textPart,
			}
			textEntryDTOs = append(textEntryDTOs, normalTextEntryDTO)
		}
	}

	return dto.RichTextDTO{
		TextEntryDTOs: textEntryDTOs,
	}, nil
}

func (s MessageBuilder) findPrivateKeyFromText(text string) (string, error) {
	privateKeyRegex, err := regexp.Compile(`^(\[.+\])`)
	if err != nil {
		return "", errors.Wrap(err, "build privateKeyRegex")
	}

	ss := privateKeyRegex.FindStringSubmatch(text)
	if len(ss) <= 1 {
		return "", nil
	}
	return ss[1], nil
}

func (s MessageBuilder) newSenderIDForUser(user model.User) types.SenderID {
	return types.SenderID(fmt.Sprintf("%d", user.ID))
}

func (s MessageBuilder) newReceiverIDForRoom(room model.Room) types.ReceiverID {
	return types.ReceiverID(fmt.Sprintf("%s/%d", "ROOM", room.ID))
}
