package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"github.com/gorilla/mux"
)

func reqPass(w http.ResponseWriter, r *http.Request) {
	defer log.PanicSafe()
	log.LogInfo("reqPass called.")
	username := r.URL.Query()["UserName"][0]
	log.LogInfo("reqPass username is %+v.", username)
	var userID string
	if &dg == nil {
		log.LogError("Discord session was nill.")
	}
	g, err := dg.GuildMembers(config.GuildID, "", 1000)
	log.LogInfo("reqPass guild is %+v.", config.GuildID)
	if err == nil {
		for _, m := range g {
			if strings.ToUpper(m.Nick) == strings.ToUpper(username) {
				for _, r := range m.Roles {
					if r == config.AdminRole {
						userID = m.User.ID
						log.LogInfo("User ID found for %+v as %+v", username, userID)
					}
				}
			}
		}
	} else {
		log.LogError("Unable to find user ID for %+v", username)
	}
	ipaddr := r.Header.Get("X-Real-IP")
	log.LogInfo("reqPass IP is %+v.", ipaddr)
	log.LogInfo(fmt.Sprintf("reqPass called:```username: %s\nip      : %s```", username, ipaddr))
	go sendPassword(userID, ipaddr)
	http.Redirect(w, r, "/login", 302)
}

func tryLogin(w http.ResponseWriter, r *http.Request) {
	defer log.PanicSafe()
	session, err := store.Get(r, "2fa")
	if err != nil {
		log.LogWarn("Error opening session for 2fa store")
	}
	vars := mux.Vars(r)
	username := vars["username"]
	password := vars["password"]
	ip := r.Header.Get("X-Real-IP")
	if len(username) == 0 {
		username = r.FormValue("UserName")
		password = r.FormValue("TempPass")
	}
	access, _ := detectUser(r, "tryLogin")
	if !access {
		log.LogDebug(fmt.Sprintf("%s is attempting login", getSessionIdentifier(r)))
		access = usePassword(username, password, ip)
		if access {
			log.LogInfo(fmt.Sprintf("%s has successfully logged in from %s", username, ip))
			log.LogDebug(fmt.Sprintf("```%+v```", session.Values))
			session.Values["username"] = username
			session.Values["ip"] = ip
			session.Values["timestamp"] = fmt.Sprintf("%+v", time.Now())
			err = session.Save(r, w)
			if err != nil {
				log.LogWarn(fmt.Sprintf("Error saving cookie. ```%+v```", err))
			}
		}
	}

	greetUser(w, r)
}

func usePassword(user string, pass string, ip string) bool {
	defer log.PanicSafe()
	log.LogInfo("%+v", toks)
	tok := toks[strings.ToUpper(user)]
	delete(toks, strings.ToUpper(user))
	if tok.IP != ip {
		log.LogWarn(fmt.Sprintf("%s attempted to use an improper IP.", user))
		return false
	}
	if tok.Password != pass {
		log.LogWarn(fmt.Sprintf("%s attempted to use an improper password. %s vs %s", user, tok.Password, pass))
		return false
	}
	if time.Since(tok.Timestamp) > (time.Minute * 5) {
		log.LogWarn("%s attempted to use expired token. \n%+v\n%+v\n%+v", user, time.Since(tok.Timestamp), tok.Timestamp, time.Now())
		return false
	}

	return true
}

func genPassword(length int) string {
	defer log.PanicSafe()
	rand.Seed(time.Now().UnixNano())
	chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
		"abcdefghijklmnopqrstuvwxyz" +
		"0123456789")
	var b strings.Builder
	for i := 0; i < length; i++ {
		b.WriteRune(chars[rand.Intn(len(chars))])
	}
	return b.String() // E.g. "ExcbsVQs"
}
func sendPassword(user string, ipaddr string) {
	defer log.PanicSafe()
	str := genPassword(15)
	log.LogInfo("sending password to %+v for %+v: %+v", ipaddr, user, str)
	m, err := dg.GuildMember(config.GuildID, user)
	if err != nil {
		log.LogErrorType(err)
	}
	now := time.Now()
	toks[strings.ToUpper(m.Nick)] = Tokens{
		Username:  user,
		IP:        ipaddr,
		Password:  str,
		Timestamp: now,
	}
	pmChann, err := dg.UserChannelCreate(user)
	if err != nil {
		log.LogErrorType(err)
	}
	dg.ChannelMessageSend(pmChann.ID, fmt.Sprintf("A temporary password was requested from %s:", ipaddr))
	dg.ChannelMessageSend(pmChann.ID, fmt.Sprintf("```%s```", str))

}

func getSessionIdentifier(r *http.Request) string {
	defer log.PanicSafe()
	ipaddr := r.Header.Get("X-Real-IP")
	if ipaddr == "" {
		ipaddr = r.RemoteAddr
	}
	uri := r.URL.Path
	return fmt.Sprintf("%s:%s", ipaddr, uri)
}

func detectUser(r *http.Request, callFunc string) (bool, string) {
	defer log.PanicSafe()
	log.LogInfo(fmt.Sprintf("%s called detectUser", getSessionIdentifier(r)))
	session, err := store.Get(r, "2fa")
	if err != nil {
		log.LogDebug(fmt.Sprintf("Unable to open 2fa session in %s", callFunc))
	}
	if session.Values["username"] != nil {
		return true, fmt.Sprintf("%s", session.Values["username"])
	}
	return false, ""
}
