package api

import (
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/http"

	usersvc "github.com/tidbcloud/account/internel/svc/user/session"

	"github.com/form3tech-oss/jwt-go"
	"github.com/tidbcloud/account/commons/config"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type RevokeLoginStateClaims struct {
	jwt.StandardClaims
	UserID            string `json:"user_id"`
	Email             string `json:"email"`
	LastPasswordReset string `json:"last_password_reset"`
}

type callbackBody struct {
	SessionToken string `json:"session_token"`
}

func Auth0CallbackRevokeUserLoginStateHandler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	body, err := parseCallbackBody(ctx, r)
	if err != nil {
		tilog.Info(ctx, "Auth0CallbackRevokeUserLoginStateHandler parse req body error", zap.Any("err", err))
		marshal, _ := json.Marshal(ActionErrRet{
			ErrMessage: err.Error(),
		})
		w.WriteHeader(http.StatusUnauthorized)
		_, _ = w.Write(marshal)
		return
	}
	tilog.Info(ctx, "Auth0CallbackRevokeUserLoginStateHandler receive callback request", zap.Any("req", body))

	claims, err := parseRevokeClaims(ctx, body.SessionToken)
	tilog.Info(ctx, "Auth0CallbackRevokeUserLoginStateHandler parseRevokeClaims result", zap.Any("claims", claims), zap.Any("err", err))
	if err != nil {
		tilog.Info(ctx, "Auth0CallbackRevokeUserLoginStateHandler parseRevokeClaims error", zap.Any("body", body), zap.Any("err", err))
		marshal, _ := json.Marshal(ActionErrRet{
			ErrMessage: err.Error(),
		})
		w.WriteHeader(http.StatusUnauthorized)
		_, _ = w.Write(marshal)
		return
	}

	err = usersvc.GetUserSessionSvc().RevokeLoginSessionByEmail(ctx, claims.Email)
	if err != nil {
		tilog.Error(r.Context(), "Auth0CallbackRevokeUserLoginStateHandler parse last_password_reset error", zap.Any("last_password_reset", claims.LastPasswordReset), zap.Any("err", err))
		marshal, _ := json.Marshal(ActionErrRet{
			ErrMessage: "last_password_reset invalid",
		})
		w.WriteHeader(http.StatusUnauthorized)
		_, _ = w.Write(marshal)
		return
	}

	w.WriteHeader(http.StatusOK)
	tilog.Info(ctx, "Auth0CallbackRevokeUserLoginStateHandler complete", zap.Any("email", claims.Email))
}

func parseRevokeClaims(ctx context.Context, token string) (*RevokeLoginStateClaims, error) {
	key := config.Global().SSOConfig.ActionKey
	claims, err := jwt.ParseWithClaims(token, &RevokeLoginStateClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(key), nil
	})
	if err != nil {
		return nil, err
	}
	if claims == nil {
		return nil, errors.New("get claims is nil")
	}
	if !claims.Valid {
		return nil, errors.New("claims is invalid")
	}
	revokeClaims, ok := claims.Claims.(*RevokeLoginStateClaims)
	if !ok {
		return nil, errors.New("convert claim to revoke login state claims fail")
	}
	return revokeClaims, nil
}

func parseCallbackBody(ctx context.Context, r *http.Request) (*callbackBody, error) {
	bodyBytes, err := io.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = r.Body.Close()
	}()
	var body callbackBody
	err = json.Unmarshal(bodyBytes, &body)
	if err != nil {
		return nil, err
	}
	return &body, nil
}
