package http

import (
	"net/url"
	"time"

	"quyun/app/models"
	"quyun/database/fields"
	"quyun/database/schemas/public/model"
	"quyun/pkg/utils"
	"quyun/providers/jwt"
	"quyun/providers/wechat"

	"github.com/gofiber/fiber/v3"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

const (
	StatePrefix = "sns_basic_auth"
	salt        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)

// @provider
type auth struct {
	wechat *wechat.Client
	jwt    *jwt.JWT
}

// @Router	/auth/login [get]
// @Bind	code query
// @Bind	state query
// @Bind	redirect query
func (ctl *auth) Login(ctx fiber.Ctx, code, state, redirect string) error {
	log.Debugf("code: %s, state: %s", code, state)

	// get the openid
	token, err := ctl.wechat.AuthorizeCode2Token(code)
	if err != nil {
		return errors.Wrap(err, "failed to get openid")
	}
	log.Debugf("tokenInfo %+v", token)

	stableToken, err := ctl.wechat.GetStableAccessToken()
	if err != nil {
		return errors.Wrap(err, "failed to get stable access token")
	}
	log.Infof("stableToken %+v", stableToken)

	authUserInfo, err := ctl.wechat.AuthorizeUserInfo(token.AccessToken, token.Openid)
	if err != nil {
		log.Error("failed to get user info: ", err)
		authUserInfo = &wechat.AuthorizeUserInfo{
			City:       "",
			Country:    "",
			Headimgurl: utils.RandomAvatar(),
			Nickname:   utils.RandomNickname(),
			Openid:     token.Openid,
			Privilege:  []string{},
			Province:   "",
			Sex:        0,
			Unionid:    "",
		}
	}

	log.Debugf("Auth User Info: %+v", authUserInfo)

	userModel := &model.Users{
		Status: fields.UserStatusOk,
		OpenID: token.GetOpenID(),
		// Username: fmt.Sprintf("u_%s", gonanoid.MustGenerate(salt, 8)),
		Username: authUserInfo.Nickname,
		Avatar:   &authUserInfo.Headimgurl,
		Metas: fields.ToJson(fields.UserMetas{
			City:         authUserInfo.City,
			Country:      authUserInfo.Country,
			HeadImageUrl: authUserInfo.Headimgurl,
			Nickname:     authUserInfo.Nickname,
			Privilege:    authUserInfo.Privilege,
			Province:     authUserInfo.Province,
			Sex:          authUserInfo.Sex,
		}),
		AuthToken: fields.ToJson(fields.UserAuthToken{
			StableAccessToken: stableToken.AccessToken,
			StableExpiresAt:   time.Now().Add(time.Second * time.Duration(stableToken.ExpiresIn)),
			AccessToken:       token.AccessToken,
			ExpiresAt:         time.Now().Add(time.Second * time.Duration(token.ExpiresIn)),
			IsSnapshotuser:    token.IsSnapshotuser,
			RefreshToken:      token.RefreshToken,
			Scope:             token.Scope,
		}),
	}
	user, err := models.Users.GetUserByOpenIDOrCreate(ctx.Context(), token.GetOpenID(), userModel)
	if err != nil {
		return errors.Wrap(err, "failed to get user by openid")
	}

	jwtToken, err := ctl.jwt.CreateToken(ctl.jwt.CreateClaims(jwt.BaseClaims{UserID: user.ID}))
	if err != nil {
		return errors.Wrap(err, "failed to create token")
	}

	ctx.Cookie(&fiber.Cookie{
		Name:     "token",
		Value:    jwtToken,
		Expires:  time.Now().Add(24 * time.Hour),
		HTTPOnly: true,
	})

	return ctx.Redirect().To(redirect)
}

// @Router	/auth/wechat [get]
// @Bind	redirect query
func (ctl *auth) Wechat(ctx fiber.Ctx, redirect string) error {
	log.Debugf("%s, query: %v", ctx.OriginalURL(), ctx.Queries())

	// 添加 redirect 参数
	fullUrl := utils.FullURI(ctx)
	u, err := url.Parse(fullUrl)
	if err != nil {
		return err
	}
	query := u.Query()
	query.Set("redirect", redirect)
	u.RawQuery = query.Encode()
	u.Path = "/v1/auth/login"
	fullUrl = u.String()

	log.Debug("redirect_uri: ", fullUrl)

	to, err := ctl.wechat.ScopeAuthorizeURL(
		wechat.ScopeAuthorizeURLWithRedirectURI(fullUrl),
	)
	if err != nil {
		return errors.Wrap(err, "failed to get wechat auth url")
	}

	return ctx.Redirect().To(to.String())
}
