package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/tidbcloud/account/central/utils"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/account/commons/config"
	"github.com/tidbcloud/account/internel/svc/user/session"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

const (
	cookieMaxAge        = 60 * 60 * 10
	cookiePath          = "/"
	devCookieDomain     = "us-west-2.nightly.shared.aws.tidbcloud.com"
	stagingCookieDomain = "us-west-2.staging.shared.aws.tidbcloud.com"
	prodCookieDomain    = "us-west-2.prod.aws.tidbcloud.com"
)

func UserLoginConfirmHandler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	bodyBytes, _ := io.ReadAll(r.Body)
	req := loginReq{}
	err := json.Unmarshal(bodyBytes, &req)
	if err != nil {
		tilog.Info(ctx, "UserLoginConfirmHandler request ShouldBindJSON error", zap.Any("err", err))
		w.WriteHeader(http.StatusBadRequest)
		marshal, _ := json.Marshal(loginRspBody("invalid body"))
		_, _ = w.Write(marshal)
		return
	}
	tilog.Info(ctx, "UserLoginConfirmHandler request", zap.Any("req", req))
	if req.IDToken == "" {
		tilog.Info(ctx, "UserLoginConfirmHandler id token is blank")
		w.WriteHeader(http.StatusBadRequest)
		marshal, _ := json.Marshal(loginRspBody("invalid body"))
		_, _ = w.Write(marshal)
		return
	}

	sid, err := session.GetUserSessionSvc().OpenLoginSession(ctx, req.IDToken)
	if err != nil {
		var status int
		var obj gin.H
		if errors.Is(err, errs.ErrInternalUnknown) {
			status = http.StatusInternalServerError
			obj = loginRspBody(fmt.Sprintf("internal error: %s", err.Error()))
		} else {
			status = http.StatusUnauthorized
			obj = loginRspBody(fmt.Sprintf("invalid request: %s", err.Error()))

		}
		w.WriteHeader(status)
		marshal, _ := json.Marshal(obj)
		_, _ = w.Write(marshal)
		return
	}

	setCookie(w, sid, cookieMaxAge)
	w.WriteHeader(http.StatusOK)
	marshal, _ := json.Marshal(loginRspBody("login success"))
	_, _ = w.Write(marshal)
}

func setCookie(w http.ResponseWriter, value string, maxAge int) {
	http.SetCookie(w, &http.Cookie{
		Name:     utils.GetCookieName(),
		Value:    url.QueryEscape(value),
		MaxAge:   maxAge,
		Path:     cookiePath,
		Domain:   cookieDomain(),
		SameSite: http.SameSiteNoneMode,
		Secure:   true,
		HttpOnly: true,
	})
}

func UserLogoutHandler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	cookie, err := r.Cookie(utils.GetCookieName())
	if err != nil {
		tilog.Info(ctx, "UserLogoutHandler get cookie from request error", zap.Any("err", err))
		w.WriteHeader(http.StatusBadRequest)
		marshal, _ := json.Marshal(loginRspBody("not found cookie"))
		_, _ = w.Write(marshal)
		return
	}
	sid, _ := url.QueryUnescape(cookie.Value)
	tilog.Info(ctx, "UserLogoutHandler request", zap.Any("sid", sid))
	if sid == "" {
		tilog.Info(ctx, "UserLogoutHandler cookie value is blank")
		w.WriteHeader(http.StatusBadRequest)
		marshal, _ := json.Marshal(loginRspBody("cookie value is blank"))
		_, _ = w.Write(marshal)
		return
	}

	err = session.GetUserSessionSvc().RevokeLoginSessionBySessionID(ctx, sid)
	if err != nil {
		tilog.Warn(ctx, "UserLogoutHandler RevokeLoginSessionBySessionID error", zap.Any("sid", sid), zap.Any("err", err))
		w.WriteHeader(http.StatusInternalServerError)
		marshal, _ := json.Marshal(loginRspBody("internal error"))
		_, _ = w.Write(marshal)
		return
	}

	setCookie(w, sid, -1)
	w.WriteHeader(http.StatusOK)
	marshal, _ := json.Marshal(loginRspBody("logout success"))
	_, _ = w.Write(marshal)
}

func cookieDomain() string {
	if config.IsProdEnv() {
		return prodCookieDomain
	} else if config.IsStagingEnv() {
		return stagingCookieDomain
	} else {
		return devCookieDomain
	}
}

func loginRspBody(msg string) gin.H {
	return gin.H{
		"message": msg,
	}
}

type loginReq struct {
	IDToken string `json:"id_token"`
}
