package repository

import (
	"context"
	dto "github.com/tidbcloud/auth-server/internal/dto/oauth2"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"time"

	"github.com/tidbcloud/auth-server/internal/entity"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func CreateOauth2AccessToken(ctx context.Context, tx *gorm.DB, token *entity.Oauth2AccessToken) error {
	return token.Create(tx)
}

func UpdateOauth2AccessToken(ctx context.Context, tx *gorm.DB, token *entity.Oauth2AccessToken, schemas []entity.Oauth2AccessTokenDBSchemaField) error {
	now := time.Now()
	schemas = append(schemas, entity.Oauth2AccessTokenDBSchema.UpdatedAt)
	token.UpdatedAt = &now
	return token.Update(tx, schemas...)
}

func RevokeOauth2AccessToken(ctx context.Context, tx *gorm.DB, id entity.ID) error {
	now := time.Now()
	return entity.NewOauth2AccessTokenQuerySet(tx).IDEq(id).GetUpdater().SetRevokedAt(&now).SetUpdatedAt(&now).Update()
}

func RevokeOauth2AccessTokenByFilter(ctx context.Context, tx *gorm.DB, param dto.RevokeTokenFilter) error {
	now := time.Now()
	qs := entity.NewOauth2AccessTokenQuerySet(tx)
	if param.ApplicationId == 0 && param.UserId == 0 && param.TokenId == 0 && param.UserEmail == "" && param.OrgId == 0 && param.GrantType == "" {
		tilog.Info(ctx, "RevokeTokenFilter is empty ,ignore revoke aciton")
		return errs.NewBadRequest("RevokeTokenFilter Params must not empty", nil)
	}

	if param.ApplicationId != 0 {
		qs.ApplicationIDEq(param.ApplicationId)
	}
	if param.UserId != 0 {
		qs.UserIDEq(param.UserId)
	}
	if param.UserEmail != "" {
		qs.UserEmailEq(param.UserEmail)
	}
	if param.TokenId != 0 {
		qs.IDEq(entity.ID(param.TokenId))
	}

	if param.OrgId != 0 {
		qs.OrgIDEq(param.OrgId)
	}
	if param.GrantType != "" {
		qs.GrantTypeEq(param.GrantType)
	}

	return qs.GetUpdater().SetRevokedAt(&now).SetUpdatedAt(&now).Update()
}

func GetOauth2AccessTokenByAccessToken(ctx context.Context, tx *gorm.DB, accessToken string) (*entity.Oauth2AccessToken, error) {
	var token entity.Oauth2AccessToken
	err := entity.NewOauth2AccessTokenQuerySet(tx).AccessTokenEq(accessToken).One(&token)
	return &token, err
}

func GetOauth2AccessTokenByRefreshToken(ctx context.Context, tx *gorm.DB, refreshToken string) (*entity.Oauth2AccessToken, error) {
	var token entity.Oauth2AccessToken
	err := entity.NewOauth2AccessTokenQuerySet(tx).RefreshTokenEq(refreshToken).One(&token)
	return &token, err
}

func GetOauth2AccessTokenByRefreshTokenWithLock(ctx context.Context, tx *gorm.DB, refreshToken string) (*entity.Oauth2AccessToken, error) {
	var obj entity.Oauth2AccessToken
	err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("refresh_token = ?", refreshToken).First(&obj).Error
	return &obj, err
}

func GetUserLastAccessToken(ctx context.Context, tx *gorm.DB, filter dto.TokenFilter) (entity.Oauth2AccessToken, error) {
	tokenResp := entity.Oauth2AccessToken{}
	var tokens []entity.Oauth2AccessToken
	qs := entity.NewOauth2AccessTokenQuerySet(tx)
	if filter.UserEmail != "" {
		qs.UserEmailEq(filter.UserEmail)
	}
	if filter.UserId != 0 {
		qs.UserIDEq(filter.UserId)
	}

	if filter.OrgId != 0 {
		qs.OrgIDEq(filter.OrgId)
	}
	if filter.ApplicationId != 0 {
		qs.ApplicationIDEq(filter.ApplicationId)
	}

	qs.RevokedAtIsNull()
	err := qs.OrderDescByCreatedAt().Limit(1).All(&tokens)
	if err == nil && len(tokens) == 1 {
		tokenResp = tokens[0]
	}

	return tokenResp, err
}

func ListOauth2AccessToken(ctx context.Context, tx *gorm.DB, filter dto.InnerQueryTokenRequest) []dto.InnerQueryToken {
	var tokens []dto.InnerQueryToken

	/*
		    select t.user_id, t.user_email ,t.org_id, t.created_at , a.id as application_id, a.name, a.logo_uri
			from auth_server_oauth2_access_tokens t
			left join auth_server_oauth2_applications a  on t.application_id=a.id
			where t.revoked_at is  not null
	*/
	tx = tx.Table("auth_server_oauth2_access_tokens t").
		Select("t.id, t.user_id, t.user_email,t.org_id, t.created_at , t.grant_type,  a.id as application_id, a.name as application_name, a.logo_uri as application_logo_uri").
		Joins("left join auth_server_oauth2_applications a on t.application_id=a.id")
	tx.Where("t.revoked_at is  not null")
	if filter.UserEmail != "" {
		tx.Where("t.user_email=?", filter.UserEmail)

	}
	if filter.GetParsedUserID() != 0 {
		tx.Where("t.user_id=?", filter.UserID)
	}
	if filter.ApplicationID != 0 {
		tx.Where("t.application_id=?", filter.ApplicationID)
	}

	if filter.OrgID != "" {
		tx.Where("t.org_id=?", filter.GetParsedOrgID())
	}
	tx = tx.Order("t.application_id asc")
	tx.Scan(&tokens)
	return tokens
}
