package fire

import (
	"context"
	"net/http"
	"strings"

	firebase "firebase.google.com/go"
	"git.mokkon.com/sainw/server_helper/util"
)

// ContextKey to be used as context key type
type ContextKey string

// UserClaimIDContextKey context key for auth id
const UserClaimIDContextKey = ContextKey("User Claim ID")

// UserClaim holds user information
type UserClaim struct {
	UserID      string
	AuthID      string
	PhoneNumber string
	Status      string
	Privileges  []string
}

// User holds user information
type User struct {
	ID          string   `json:"id"`
	UserName    string   `json:"user_name"`
	PhoneNumber string   `json:"phone_number"`
	Status      string   `json:"status"`
	SysAdmin    bool     `json:"sys_admin"`
	Privileges  []string `json:"privileges"`
}

// GetUserClaim returns UserClaim from context
func GetUserClaim(ctx context.Context) (UserClaim, bool) {
	userClaim, ok := ctx.Value(UserClaimIDContextKey).(UserClaim)
	return userClaim, ok
}

func (c UserClaim) HasPrivileges(ps ...string) bool {
	for _, p := range ps {
		if util.Contains(c.Privileges, p) {
			return true
		}
	}
	return false
}

func Auth(app *firebase.App, h http.Handler) http.Handler {
	fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// read token
		token := r.Header.Get("Token")
		ctx := r.Context()
		if token == "" {
			util.WriteResponse(w, http.StatusUnauthorized, "Error", "invalid token")
			return
		}
		client, err := app.Auth(ctx)
		if err != nil {
			util.WriteResponse(w, http.StatusInternalServerError, "Error", err.Error())
			return
		}
		authToken, err := client.VerifyIDToken(ctx, token)
		if err != nil {
			util.WriteResponse(w, http.StatusUnauthorized, "Error", "unverifiable idToken")
			return
		}

		var privileges []string
		if str, ok := authToken.Claims["pr"].(string); ok {
			privileges = strings.Split(str, ":")
		}
		userID := ""
		if str, ok := authToken.Claims["cid"].(string); ok {
			userID = str
		}
		phoneNumber := ""
		if str, ok := authToken.Claims["phone_number"].(string); ok {
			phoneNumber = str
		}
		status := ""
		if str, ok := authToken.Claims["st"].(string); ok {
			status = str
		}

		userClaim := UserClaim{AuthID: authToken.UID, UserID: userID,
			PhoneNumber: phoneNumber, Privileges: privileges, Status: status}
		ctx = context.WithValue(ctx, UserClaimIDContextKey, userClaim)
		r = r.WithContext(ctx)
		h.ServeHTTP(w, r)
	})
	return fn
}