package main

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/rudi9719/loggy"
)

var (
	startupTime    time.Time
	setupToken     = fmt.Sprintf("%+v", rand.Intn(9999)+1000)
	rebootToken    = fmt.Sprintf("%+v", rand.Intn(9999)+1000)
	bump           = true
	config         Config
	log            = loggy.NewLogger(config.LogOpts)
	lastActiveTime time.Time
	token          string
	configFile     string
	dg             *discordgo.Session
	lastPM         = make(map[string]time.Time)
	introMsg       = make(map[string]string)
	quotes         = []string{"The hardest choices require the strongest wills.", "You're strong, but I could snap my fingers and you'd all cease to exist.", "Fun isn't something one considers when balancing the universe. But this... does put a smile on my face.", "Perfectly balanced, as all things should be.", "I am inevitable."}
	gitCommit      string
	commands       []Command
)

func init() {
	flag.StringVar(&token, "t", "", "Bot Token")
	flag.StringVar(&configFile, "c", "", "Config file")
	flag.Parse()
}

func main() {
	go runWeb()
	defer log.PanicSafe()
	if configFile == "" {
		configFile = "config.json"
	} else {
		loadConfig()
	}
	log = loggy.NewLogger(config.LogOpts)
	startupTime = time.Now()
	lastActiveTime = time.Now()
	if token == "" {
		log.LogPanic("No token provided. Please run: disgord-thanos -t <bot token>")
	}

	log.LogCritical("SetupToken: %+v\nRebootToken: %+v", setupToken, rebootToken)
	var err error
	dg, err = discordgo.New("Bot " + token)
	if err != nil {
		log.LogErrorType(err)
		log.LogPanic("Unable to create bot using token.")
	}

	dg.AddHandler(ready)
	dg.AddHandler(guildMemberRemove)
	dg.AddHandler(guildMemberAdd)
	dg.AddHandler(guildMemberBanned)
	go setupCommands()
	dg.AddHandler(messageCreate)
	dg.AddHandler(readReaction)
	dg.AddHandler(guildMemberUpdate)
	dg.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAll)

	err = dg.Open()
	if err != nil {
		log.LogErrorType(err)
		log.LogPanic("Unable to open websocket.")
	}

	log.LogInfo("Thanos is now running.  Press CTRL-C to exit.")
	go purgeTimer(dg)
	go rebootBump()
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
	<-sc
	saveConfig()
	dg.Close()
}

func exit(s *discordgo.Session) {
	s.Close()
	saveConfig()
	os.Exit(0)
}

func runPurge(s *discordgo.Session) {
	defer log.PanicSafe()
	if time.Since(config.BumpTime) > 2*time.Hour {
		bump = true
	}
	for uid, join := range config.Probations {
		if time.Since(join) > 2*time.Hour {
			delete(config.Probations, uid)
			s.ChannelMessageDelete(config.IntroChann, introMsg[uid])
		}
	}
	for k, v := range config.Unverified {
		isUnverified := false
		m, err := s.GuildMember(config.GuildID, k)
		if err != nil {
			delete(config.Unverified, k)
			continue
		}
		for _, role := range m.Roles {
			if role == config.MonitorRole {
				isUnverified = true
			}
		}
		if isUnverified {
			if val, ok := lastPM[k]; ok && time.Since(val) < 5*time.Minute {
				continue
			}
			lastPM[k] = time.Now()
			pmChann, _ := s.UserChannelCreate(k)
			s.ChannelMessageSend(pmChann.ID,
				fmt.Sprintf("This is a reminder that you have not verified with me and will be removed at <t:%+v:t>. You may reply to this message for verification instructions.", v.Add(1*time.Hour).Unix()))
			if time.Since(v) > (time.Hour * 1) {
				s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v was removed.", m.Mention()))
				s.GuildMemberDeleteWithReason(config.GuildID, k, fmt.Sprintf("Unverified user %+v.", v))
			}
		} else {
			delete(config.Unverified, k)
		}
	}
	messages, _ := s.ChannelMessages(config.MonitorChann, 100, "", "", "")
	for _, message := range messages {
		found := false
		for user := range config.Unverified {
			if message.Author.ID == user {
				found = true
			}
			for _, mention := range message.Mentions {
				if mention.ID == user {
					found = true
				}
			}
		}
		if !found {
			s.ChannelMessageDelete(config.MonitorChann, message.ID)
		}
	}
	go cleanSocials(s)
	saveConfig()
}

func cleanSocials(s *discordgo.Session) {
	for _, channel := range config.SocialChanns {
		go func(channel string, s *discordgo.Session) {
			messages, _ := s.ChannelMessages(channel, 100, "", "", "")
			for _, message := range messages {
				_, err := s.GuildMember(config.GuildID, message.Author.ID)
				if err != nil {
					s.ChannelMessageDelete(channel, message.ID)
				}
			}
		}(channel, s)
	}
}

func verifyMember(s *discordgo.Session, u discordgo.User) {
	defer log.PanicSafe()
	log.LogDebug("Adding verified roll")
	s.GuildMemberRoleAdd(config.GuildID, u.ID, config.VerifiedRole)
	log.LogDebug("Removing monitor role")
	s.GuildMemberRoleRemove(config.GuildID, u.ID, config.MonitorRole)
	log.LogDebug("Creating PM channel")
	st, err := s.UserChannelCreate(u.ID)
	if err != nil {
		log.LogErrorType(err)
	}
	log.LogDebug("Sending acceptance message!")
	s.ChannelMessageSend(st.ID, "Your verification has been accepted, welcome!")
	log.LogDebug("Sending Intro message")
	m, err := s.ChannelMessageSend(config.IntroChann, fmt.Sprintf("Welcome %+v please introduce yourself! :) feel free to check out <#710557387937022034> to tag your roles. Also please mute any channels you are not interested in!", u.Mention()))
	if err != nil {
		log.LogErrorType(err)
	}
	log.LogDebug("Storing introMsg ID to be deleted later")
	introMsg[u.ID] = m.ID
}

func rejectVerification(s *discordgo.Session, u discordgo.User) {
	defer log.PanicSafe()
	st, err := s.UserChannelCreate(u.ID)
	if err != nil {
		log.LogErrorType(err)
	}
	if st != nil {
		s.ChannelMessageSend(st.ID, fmt.Sprintf("Your verification has been rejected. This means it did not clearly show your face, with your pinkie finger held to the corner of your mouth, or the photo looked edited/filtered. No filters will be accepted.\n\nPlease try again before <t:%+v:t>", time.Now().Add(1*time.Hour).Unix()))
	}
	config.Unverified[u.ID] = time.Now()
}

func requestAge(s *discordgo.Session, u discordgo.User) {
	defer log.PanicSafe()
	st, err := s.UserChannelCreate(u.ID)
	if err != nil {
		log.LogErrorType(err)
	}
	s.ChannelMessageSend(st.ID, "What is your ASL? (Age/Sex/Language) Please note, this is NOT requesting your gender, but your biological sex. Gender is a social construct, sex is biology and in the context of pornographic images more important.")

}