package member

import (
	"context"
	"crypto/rsa"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"time"

	"encoding/json"

	"git.loafle.net/commons_go/logging"
	"git.loafle.net/overflow/overflow_server_app/commons"
	"git.loafle.net/overflow/overflow_server_app/external/grpc"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/valyala/fasthttp"
)

var (
	verifyKey *rsa.PublicKey
	signKey   *rsa.PrivateKey
)

func init() {
	signBytes, err := ioutil.ReadFile("overFlow-private.key")
	if nil != err {
		panic(err)
	}

	signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
	if nil != err {
		panic(err)
	}

	verifyBytes, err := ioutil.ReadFile("overFlow-public.pem")
	if nil != err {
		panic(err)
	}

	verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
	if nil != err {
		panic(err)
	}
}

func Signin(ctx *fasthttp.RequestCtx) {
	var err error
	var webParams map[string]interface{}

	webBytes := ctx.PostBody()
	err = json.Unmarshal(webBytes, &webParams)

	if err != nil {
		commons.SendRESTError(ctx, fasthttp.StatusBadRequest, err)
		// fmt.Fprintf(ctx, "Err!!!!: %s\n", err)
		return
	}

	length := len(webParams)

	if length < 0 {
		fmt.Println("eeee")
	}

	signinId := webParams["signinId"].(string)
	signinPw := webParams["signinPw"].(string)

	params := []string{signinId, signinPw}

	gRPCCtx := context.Background()
	r, err := grpc.Exec(gRPCCtx, "MemberService.signin", params)
	if nil != err {
		commons.SendRESTError(ctx, fasthttp.StatusBadRequest, err)
		return
	}

	token := jwt.New(jwt.SigningMethodRS512)

	/* Create a map to store our claims */
	claims := token.Claims.(jwt.MapClaims)

	// expireTime := time.Now().Add(time.Hour * 24)

	/* Set token claims */
	claims["iss"] = "overFlow"
	claims["iat"] = time.Now().Unix()
	claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
	claims["aud"] = "www.overflow.cloud"
	claims["sub"] = signinId

	/* Sign the token with our secret */
	tokenString, _ := token.SignedString(signKey)

	log.Printf("%s \n", tokenString)

	var domainMember interface{}
	err = json.Unmarshal([]byte(r), &domainMember)

	signInResult := &SignInResult{
		AuthToken:    tokenString,
		DomainMember: domainMember,
	}

	// var jwtCookie fasthttp.Cookie
	// jwtCookie.SetKey("AuthToken")
	// jwtCookie.SetValue(tokenString)
	// // jwtCookie.SetHTTPOnly(true)
	// // jwtCookie.SetSecure(true)
	// // jwtCookie.SetDomain("127.0.0.1")
	// jwtCookie.SetExpire(expireTime)
	// jwtCookie.SetPath("/")

	commons.SendRESTResponse(ctx, signInResult)
}

type SignInResult struct {
	AuthToken    string      `json:"authToken"`
	DomainMember interface{} `json:"domainMember"`
}

func SigninByCookie(ctx *fasthttp.RequestCtx) {
	var err error
	var webParams map[string]interface{}

	webBytes := ctx.PostBody()
	err = json.Unmarshal(webBytes, &webParams)

	if err != nil {
		commons.SendRESTError(ctx, fasthttp.StatusBadRequest, err)
		return
	}

	length := len(webParams)

	if length < 0 {
		fmt.Println("eeee")
	}

	authToken := webParams["authToken"].(string)

	if authToken == "" {
		err = errors.New("authToken is not exist")
		commons.SendRESTError(ctx, fasthttp.StatusBadRequest, err)
		return
	}

	token, err := jwt.Parse(authToken, func(token *jwt.Token) (interface{}, error) {
		// Don't forget to validate the alg is what you expect:
		if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
			return nil, fmt.Errorf("Webapp: Unexpected signing method: %v", token.Header["alg"])
		}

		// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
		return verifyKey, nil
	})

	if nil != err {
		commons.SendRESTError(ctx, fasthttp.StatusBadRequest, err)
		return
	}

	var ok bool
	var claims jwt.MapClaims
	if claims, ok = token.Claims.(jwt.MapClaims); !ok || !token.Valid {
		logging.Logger().Warnf("Webapp: Token is not valid %v", token)

		err = errors.New("authToken is invalid")
		commons.SendRESTError(ctx, fasthttp.StatusBadRequest, err)
		return
	}

	params := []string{claims["sub"].(string)}

	gRPCCtx := context.Background()
	r, err := grpc.Exec(gRPCCtx, "DomainMemberService.readByMemberEmail", params)
	if nil != err {
		commons.SendRESTError(ctx, fasthttp.StatusBadRequest, err)
		return
	}

	commons.SendRESTResponse(ctx, r)
}