package main

import (
	"math"
	"time"

	"github.com/go-errors/errors"
	tele "gopkg.in/telebot.v3"
)

func isFromAdmin(sender *tele.User) bool {
	if sender == nil {
		return false
	}

	_, ok := config.adminUidLookup[sender.ID]
	return ok
}

func initBot() (*tele.Bot, error) {
	pref := tele.Settings{
		Token:  config.TGBotToken,
		Poller: &tele.LongPoller{Timeout: 15 * time.Second},
	}

	b, err := tele.NewBot(pref)
	if err != nil {
		return nil, errors.Wrap(err, 0)
	}

	b.Use(logMiddleware)

	// command routing
	b.Handle("/start", handleStartCmd)
	b.Handle("/me", handleUserInfoCmd)
	b.Handle("/chat", handleChatInfoCmd)

	b.Handle("/year_progress", handleYearProgressCmd)
	b.Handle("/xr", handleExchangeRateCmd)

	b.Handle("/reason", handleReasonCmd)
	b.Handle("/tr", handleTranslateCmd)
	for _, tbtn := range translateBtns {
		b.Handle(tbtn, handleTranslateBtn)
	}
	b.Handle("/kanji", handleKanjiCmd)

	b.Handle(tele.OnText, handleGeneralMessage)
	b.Handle(tele.OnSticker, handleGeneralMessage)

	// admin required
	adminGrp := b.Group()
	adminGrp.Use(adminMiddleware)
	adminGrp.Handle("/traffic", handleTrafficCmd)
	adminGrp.Handle(&trafficBtnDays, handleTrafficBtnDays)
	adminGrp.Handle(&trafficBtnMonths, handleTrafficBtnMonths)

	adminGrp.Handle("/dig", handleDigCmd)

	return b, nil
}

func adminMiddleware(next tele.HandlerFunc) tele.HandlerFunc {
	return func(c tele.Context) error {
		u := c.Sender()
		if u == nil {
			if cb := c.Callback(); cb != nil {
				u = cb.Sender
			}
		}
		if !isFromAdmin(u) {
			return nil
		}
		return next(c)
	}
}

func logMiddleware(next tele.HandlerFunc) tele.HandlerFunc {
	return func(c tele.Context) error {
		upd := c.Update()
		defer func() {
			logger.Debugw("Log middleware", "update", upd)
		}()

		return next(c)
	}
}

func drawBar(progress float64, length int) string {
	barChars := []rune("ยท-=")

	if length <= 0 {
		return ""
	}
	step := 1 / float64(length)
	buf := make([]rune, length+2)
	buf[0], buf[length+1] = '[', ']'
	for i := 0; i < length; i++ {
		fill := (progress - float64(i)*step) / step
		fill = math.Min(math.Max(fill, 0), 1)
		idx := int(math.Round(fill * float64(len(barChars)-1)))
		buf[i+1] = barChars[idx]
	}
	return string(buf)
}

func handleGeneralMessage(c tele.Context) error {
	if thread := matchAssistantConversation(c.Bot().Me, c.Message()); thread != nil {
		return handleAssistantConversation(c, thread)
	}

	return nil
}

func stickerFromID(id string) *tele.Sticker {
	return &tele.Sticker{
		File: tele.File{
			FileID: id,
		},
	}
}

func setTyping(c tele.Context) chan error {
	resultCh := make(chan error, 1)
	go func() {
		defer close(resultCh)
		resultCh <- c.Bot().Notify(c.Chat(), tele.Typing)
	}()
	return resultCh
}