package account

import (
	"crypto/rand"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"log"
	"reflect"
	"strings"

	"git.trj.tw/golang/go-gallery/modules/memstore"

	"git.trj.tw/golang/go-gallery/models"
	"git.trj.tw/golang/go-gallery/modules/context"
	"git.trj.tw/golang/go-gallery/modules/utils"
	"golang.org/x/crypto/pbkdf2"
)

var passIterator = 2048
var passLen = 64
var passSaltLen = 16
var passHash = sha512.New

// UserLogin route
func UserLogin(c *context.Context) {
	loginArg := struct {
		Account  string `form:"account" json:"account" binding:"required"`
		Password string `form:"password" json:"password" binding:"required"`
	}{
		Account:  "",
		Password: "",
	}
	err := c.BindData(&loginArg)
	if err != nil {
		c.DataFormat(nil)
		return
	}

	acc, err := models.GetAccount(loginArg.Account)

	if err != nil {
		log.Println(err)
		c.ServerError(nil)
		return
	}
	if acc == nil {
		c.NotFound("User not found")
		return
	}

	strs := strings.Split(acc.Password, ".")
	if len(strs) != 2 {
		c.ServerError("store pass format error")
		return
	}
	b, err := hex.DecodeString(strs[0])
	if err != nil {
		c.ServerError(nil)
		return
	}
	hashPass, err := hex.DecodeString(strs[1])
	if err != nil {
		c.ServerError(nil)
		return
	}

	enc := pbkdf2.Key([]byte(loginArg.Password), b, passIterator, passLen, passHash)

	if enc == nil || !reflect.DeepEqual(enc, hashPass) {
		c.DataFormat("password error")
		return
	}

	res := utils.ToMap(acc)

	m := make(map[string]interface{})

	m["user"] = res

	jsonStr, err := json.Marshal(m)
	if err != nil {
		c.ServerError(nil)
		return
	}

	tByte := make([]byte, 20)
	_, err = rand.Read(tByte)
	if err != nil {
		c.ServerError(nil)
		return
	}

	err = memstore.RedisSet("golang", hex.EncodeToString(tByte), string(jsonStr), 600)
	if err != nil {
		c.ServerError(nil)
	}

	m["token"] = hex.EncodeToString(tByte)

	c.Success(m)
}

// UserLogout route
func UserLogout(c *context.Context) {
	token := c.GetHeader("X-Auth-Token")
	if len(token) == 0 {
		c.DataFormat("token not found")
		return
	}

	err := memstore.RedisDel("golang", token)
	if err != nil {
		c.ServerError("remvoe session fail")
		return
	}

	c.Success(nil)
}

// UserSignup route
func UserSignup(c *context.Context) {
	singupObj := struct {
		Account  string `json:"account" binding:"required"`
		Password string `json:"password" binding:"required"`
		Nick     string `json:"nick"`
		Email    string `json:"email" binding:"required"`
	}{}

	err := c.BindData(&singupObj)
	if err != nil {
		c.DataFormat(nil)
		return
	}

	salt := make([]byte, passSaltLen)
	_, err = rand.Read(salt)
	if err != nil {
		c.ServerError(nil)
		return
	}

	passBuf := pbkdf2.Key([]byte(singupObj.Password), salt, passIterator, passLen, passHash)
	passStr := hex.EncodeToString(salt) + "." + hex.EncodeToString(passBuf)

	dat, err := models.GetAccount(singupObj.Account)
	if err != nil {
		c.ServerError(nil)
		return
	}
	if dat != nil {
		c.DataFormat("account exists")
		return
	}
	dat = nil

	acc := &models.Account{}
	acc.Account = singupObj.Account
	acc.Nick = singupObj.Nick
	acc.Password = passStr
	acc.Email = singupObj.Email

	err = acc.Create()
	if err != nil {
		c.ServerError(nil)
		return
	}

	ok, err := acc.Get()

	if !ok {
		c.ServerError(nil)
		return
	}

	log.Println(acc.ID)

	c.Success(nil)
}
