package main

import (
	"fmt"
	"log"
	"regexp"
	"strings"

	"code.ivysaur.me/libnmdc"
	telegram "github.com/go-telegram-bot-api/telegram-bot-api"
)

func (this *NTFServer) HandleDirectMessage(update telegram.Update) error {

	// Registration workflow

	userID := int64(update.Message.From.ID)

	// Stash the telegram user ID against this direct-message chat ID so that
	// we can always reply later on
	chatID := update.Message.Chat.ID
	if oldChatID, ok := this.config.DirectMessageChats[userID]; !ok || oldChatID != chatID {
		this.config.DirectMessageChats[userID] = chatID
		err := this.config.Save(this.configFile)
		if err != nil {
			log.Printf("Couldn't save chat ID %d for user %d", chatID, userID)
		}
	}

	respond := func(str string) error {
		return this.ReplyTelegramUser(userID, str, update.Message.MessageID)
	}

	// Find out the current status for this chat ID
	hubNick, isKnown := this.config.KnownUsers[userID]
	_, isInGroupChat := this.config.GroupChatMembers[userID]

	// Handle the incoming request

	msg := update.Message.Text
	if strings.HasPrefix(msg, "/pm ") {
		if !(isKnown && isInGroupChat) {
			return respond("Can't send a native PM until you've joined.")
		}

		conn, ok := this.conns[hubNick]
		if !ok {
			return respond("Can't send a native PM (No upstream hub connection)")
		}

		parts := strings.SplitN(msg, " ", 3)
		if len(parts) != 3 {
			return respond("Expected format /pm [recipient] [message] - try again...")
		}

		if !conn.UserExists(parts[1]) {
			return respond(fmt.Sprintf("Can't PM offline user '%s'", parts[1]))
		}

		err := conn.SayPrivate(parts[1], parts[2])
		if err != nil {
			log.Printf("Sending PM %s -> %s failed because: %s", hubNick, parts[1], err.Error())
			return respond(fmt.Sprintf("Sending PM failed because: %s", err.Error()))
		}

	} else if strings.HasPrefix(msg, "/join ") {
		requestedHubNick := msg[6:]

		// Some users take the [] in the message literally. A-Za-z0-9 are the only supported characters
		requestedHubNick = regexp.MustCompile(`[^a-zA-Z0-9]`).ReplaceAllString(requestedHubNick, "")

		// Minimum nick lengths
		if len(requestedHubNick) < this.config.HubNickMinChars {
			return respond(fmt.Sprintf("Upstream nickname '%s' should be at least %d characters long", requestedHubNick, this.config.HubNickMinChars))
		}

		err := this.registerUser(userID, requestedHubNick)
		if err != nil {
			log.Printf("Failed to register user: %s", err.Error())
			return respond(fmt.Sprintf("Couldn't allow registration because: %s", err.Error()))
		}

		return respond(fmt.Sprintf("Hi '%s'! You are now registered, and can join %s at %s", requestedHubNick, this.config.HubDescription, this.inviteLink))

	} else if strings.HasPrefix(msg, "/rejoin") {
		if isKnown && !isInGroupChat {
			return respond(fmt.Sprintf("Welcome back '%s'! You can join %s at %s", hubNick, this.config.HubDescription, this.inviteLink))

		} else {
			return respond("You are either already joined (try /quit first), or not yet registered (try /join first).")
		}

	} else if strings.HasPrefix(msg, "/userlist") {
		conn, ok := this.conns[hubNick]
		if !ok {
			return respond("Can't get userlist for you (No upstream hub connection)")
		}

		usernames, err := this.getUserlistText(conn)
		if err != nil {
			return respond("Can't get userlist for you (internal error)")
		}

		return respond("Online users: " + usernames)

	} else if strings.HasPrefix(msg, "/whois ") {
		target := msg[7:]
		conn, ok := this.conns[hubNick]
		if !ok {
			return respond("Can't get user details for you (No upstream hub connection)")
		}

		var userinfo libnmdc.UserInfo
		var exists bool
		err := conn.Users(func(umap *map[string]libnmdc.UserInfo) error {
			userinfo, exists = (*umap)[target]
			return nil
		})
		if err != nil {
			log.Printf("Error retrieving userlist: %s", err.Error())
			return respond("Can't get user details for you (internal error)")
		}

		if !exists {
			return respond(fmt.Sprintf("There is no online user '%s'.", target))
		}

		return respond(fmt.Sprintf("Description: %s\nShare size: %d\nClient: %s %s", userinfo.Description, userinfo.ShareSize, userinfo.ClientTag, userinfo.ClientVersion))

	} else if strings.HasPrefix(msg, "/quit") {

		this.kickAndDrop(userID)
		return respond("Disconnected. You can register again by typing /help .")

	} else { // e.g. /start or /help
		// Help
		helpMsg := `I am a bot that connects Telegram with ` + this.config.HubDescription + ".\n"
		if isKnown {
			helpMsg += "You are currently registered as: '" + hubNick + "'\n"
		} else {
			helpMsg += "You aren't connected yet.\n"
		}
		if isInGroupChat {
			helpMsg += "You are currently in the groupchat.\n"
		} else {
			helpMsg += "You haven't joined the groupchat.\n"
		}

		helpMsg += `
		
Available commands:
/setup - Welcome message
/join [hubnick] - Register as 'hubnick' and join ` + this.config.HubDescription + `
/rejoin - Re-invite if you are already registered
/pm [recipient] [message] - Send a native PM (if connected)
/userlist - List native online users
/whois [hubnick] - Check if native user is online, and read their description
/quit - Unregister your nick and leave the group chat
`

		return respond(helpMsg)
	}

	return nil
}
