package telegram

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"go.uber.org/zap"

	"github.com/usememos/memos/common/log"
)

type Handler interface {
	BotToken(ctx context.Context) string
	MessageHandle(ctx context.Context, bot *Bot, message Message, attachments []Attachment) error
	CallbackQueryHandle(ctx context.Context, bot *Bot, callbackQuery CallbackQuery) error
}

type Bot struct {
	handler Handler
}

// NewBotWithHandler create a telegram bot with specified handler.
func NewBotWithHandler(h Handler) *Bot {
	return &Bot{handler: h}
}

const noTokenWait = 30 * time.Second
const errRetryWait = 10 * time.Second

// Start start a long polling using getUpdates to get Update, call r.MessageHandle while get new message updates.
func (b *Bot) Start(ctx context.Context) {
	var offset int64

	for {
		updates, err := b.GetUpdates(ctx, offset)
		if err == ErrInvalidToken {
			time.Sleep(noTokenWait)
			continue
		}
		if err != nil {
			log.Warn("fail to telegram.GetUpdates", zap.Error(err))
			time.Sleep(errRetryWait)
			continue
		}

		singleMessages := make([]Message, 0, len(updates))
		groupMessages := make([]Message, 0, len(updates))

		for _, update := range updates {
			offset = update.UpdateID + 1

			// handle CallbackQuery update
			if update.CallbackQuery != nil {
				err := b.handler.CallbackQueryHandle(ctx, b, *update.CallbackQuery)
				if err != nil {
					log.Error("fail to handle CallbackQuery", zap.Error(err))
				}

				continue
			}

			// handle Message update
			if update.Message != nil {
				message := *update.Message

				// skip unsupported message
				if !message.IsSupported() {
					_, err := b.SendReplyMessage(ctx, message.Chat.ID, message.MessageID, "Supported messages: animation, audio, text, document, photo, video, video note, voice, other messages with caption")
					if err != nil {
						log.Error(fmt.Sprintf("fail to telegram.SendReplyMessage for messageID=%d", message.MessageID), zap.Error(err))
					}
					continue
				}

				// Group message need do more
				if message.MediaGroupID != nil {
					groupMessages = append(groupMessages, message)
					continue
				}

				singleMessages = append(singleMessages, message)
				continue
			}
		}

		err = b.handleSingleMessages(ctx, singleMessages)
		if err != nil {
			log.Error("fail to handle singleMessage", zap.Error(err))
		}

		err = b.handleGroupMessages(ctx, groupMessages)
		if err != nil {
			log.Error("fail to handle plain text message", zap.Error(err))
		}
	}
}

var ErrInvalidToken = errors.New("token is invalid")

func (b *Bot) apiURL(ctx context.Context) (string, error) {
	token := b.handler.BotToken(ctx)
	if token == "" {
		return "", ErrInvalidToken
	}

	if strings.HasPrefix(token, "http") {
		return token, nil
	}

	return "https://api.telegram.org/bot" + token, nil
}
