package login

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/pkg/api/common"
	"github.com/pingcap-inc/clinic/pkg/api/error"
	"github.com/pingcap-inc/clinic/pkg/auth/oauth"
	"github.com/pingcap-inc/clinic/pkg/auth/session"
)

type LoginService struct {
	Session *session.SessionManager
}

func NewLoginService(session *session.SessionManager) *LoginService {
	return &LoginService{
		Session: session,
	}
}

// @Summary		Validate the user
// @Tags			OAuth
// @Description	set auth header
// @Accept			json
// @Param			name		path		string		true	"string enums"	Enums(internal,ask_tug,tidbcloud,pingkai)
// @Param			validation	body		Validation	true	"validation"
// @Success		200			{object}	ValidationResp
// @Router			/login/oauth/{name}/validate [post]
func (s *LoginService) OAuthValidate(c *gin.Context) {
	otype := c.Param("name")
	if otype == "" {
		_ = c.Error(error.ErrBadRequest.New("missing auth type parameter"))
		return
	}

	var val Validation
	if err := c.BindJSON(&val); err != nil {
		_ = c.Error(error.ErrBadRequest.New("body is required"))
		return
	}

	if val.Code == "" || val.RedirectUri == "" {
		_ = c.Error(error.ErrBadRequest.New("missing parameter"))
		return
	}

	auth, ok := oauth.Authers[oauth.OAuthType(otype)]
	if !ok {
		_ = c.Error(error.ErrBadRequest.New("unsupport oauth type"))
		return
	}

	sess, err := auth.WithRedirectURL(val.RedirectUri).Exchange(c.Request.Context(), val.State, val.Code)
	if err != nil {
		_ = c.Error(err)
		return
	}

	if sess == nil {
		_ = c.Error(error.ErrNotFound.New("not found the user"))
		return
	}

	key, err := s.Session.NewSession(sess)
	if err != nil {
		log.Error("new session failed", zap.Error(err))
		return
	}

	s.Session.SetCookie(c, key)

	c.JSON(http.StatusOK, ValidationResp{
		SessionKey: key,
	})
}

// @Summary		Config the user
// @Tags			OAuth
// @Description	get oauth config
// @Produce		json
// @Param			name			path		string	true	"string enums"	Enums(internal,ask_tug,tidbcloud,pingkai)
// @Param			redirect_uri	query		string	true	"the redirect_uri request parameter is used as a callback URL"
// @Success		200				{object}	OAuthInfoResp
// @Router			/login/oauth/{name}/info [get]
func (s *LoginService) OAuthConfig(c *gin.Context) {
	redirectUrl := c.Query("redirect_uri")
	if redirectUrl == "" {
		_ = c.Error(error.ErrBadRequest.New("missing redirect_uri parameter"))
		return
	}

	otype := c.Param("name")
	if otype == "" {
		_ = c.Error(error.ErrBadRequest.New("missing auth type parameter"))
		return
	}

	auth, ok := oauth.Authers[oauth.OAuthType(otype)]
	if !ok {
		//_ = c.Error(error.ErrBadRequest.New("unsupport oauth type"))
		c.JSON(http.StatusOK, OAuthInfoResp{
			AuthUrl: "",
		})
		return
	}

	url, err := auth.WithRedirectURL(redirectUrl).GenerateAuthCodeURL()
	if err != nil {
		_ = c.Error(err)
		return
	}

	c.JSON(http.StatusOK, OAuthInfoResp{
		AuthUrl: url,
	})
}

// @Summary		Logout
// @Tags			OAuth
// @Description	log out
// @Produce		json
// @Success		200	{object}	common.SuccessResp
// @Router			/login/logout [post]
func (s *LoginService) LoginOut(c *gin.Context) {
	s.Session.ClearCookie(c)

	c.JSON(http.StatusOK, common.SuccessResponse())
}
