package oauth2

import (
	"context"
	"net/url"

	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	dto "github.com/tidbcloud/auth-server/internal/dto/oauth2"
	"github.com/tidbcloud/auth-server/internal/entity"
	tierrs "github.com/tidbcloud/tiinf/errs"
	"gorm.io/gorm"
)

type Oauth2Service interface {
	// Get the device_code and user_code by client_id
	HandleDeviceAuthorizationRequest(ctx context.Context, clientID, clientIP, userAgent string) (*dto.DeviceAuthorizationResponse, error)

	// Exchange the device_code for access_token and refresh_token
	HandleDeviceTokenRequest(ctx context.Context, clientID, deviceCode string) (*dto.TokenResponse, error)

	// Check if user_code is valid, and return the grant request details
	HandleInnerDeviceCodeCheckRequest(ctx context.Context, userCode string) (*dto.InnerDeviceCheckResponse, tierrs.Error)

	// User approve/deny the device grant request
	HandleInnerDeviceCodeConfirmationRequest(ctx context.Context, user *account.User, userCode string, approve bool, orgId uint64) tierrs.Error

	// Check if authorization request is valid
	InnerAuthorizationCodeRequestValidate(
		ctx context.Context, user *account.User, clientID, responseType, redirectURI, state string, scopes []string,
	) (*AuthorizeRequest, tierrs.Error)

	// User approve the authorization request, and create the grant object and redirectURI
	InnerAuthorizationCodeApprove(
		ctx context.Context, user *account.User, req *AuthorizeRequest, orgID uint64, projectIDs []uint64,
	) (*entity.Oauth2CodeGrant, *url.URL, tierrs.Error)

	// Create the access_token identity by code for authorization_code grant
	CreateAccessTokenByCodeGrant(
		ctx context.Context, clientID, clientSecret, code, redirectURI string,
	) (*dto.TokenResponse, error)

	// Refresh the access_token identity by refresh_token
	HandleRefreshTokenRequest(
		ctx context.Context, clientID, clientSecret, refreshToken string,
	) (*dto.TokenResponse, error)

	// Revoke the access_token identity by access_token or refresh_token
	HandleRevokeTokenRequest(ctx context.Context, clientID, clientSecret, token, tokenTypeHint string)

	// Return the access_token identity info by raw access_token
	// Provide for the RBAC sdk
	GetAccessTokenIdentity(ctx context.Context, rawAccessToken string) (*entity.Oauth2AccessToken, tierrs.Error)

	CreateApplication(ctx context.Context, name, redirectURI string, grantType entity.Oauth2GrantType, orgRole, projectRole string) (*entity.Oauth2Application, string, error)

	GetApplicationByID(ctx context.Context, id uint64) (*entity.Oauth2Application, error)

	// query token by filter
	//HandleQueryTokenRequest(ctx context.Context, req dto.InnerQueryTokenRequest) []dto.InnerQueryToken

	InnerQueryUserAuthorizedApplications(ctx context.Context, req dto.UserAuthorizedAppsFilter) []dto.InnerApplication

	QueryOauth2Applications(ctx context.Context, filter dto.QueryAppsParam) ([]entity.Oauth2Application, error)

	RevokeTokenByFilter(ctx context.Context, req dto.RevokeTokenFilter) error

	GetUserLastAccessToken(ctx context.Context, filter dto.TokenFilter) (entity.Oauth2AccessToken, error)

	// query user info by access_token
	GetUserInfo(ctx context.Context, rawAccessToken string) (*dto.UserInfo, error)
	GetAccessTokenInfo(ctx context.Context, rawAccessToken string) (*dto.AccessTokenInfo, error)
}

type Oauth2ServiceImpl struct {
	db *gorm.DB

	accountProviderClient account.AccountAPIServiceClient
}

func NewOauth2Service(
	db *gorm.DB, accountProviderClient account.AccountAPIServiceClient,
) Oauth2Service {
	return &Oauth2ServiceImpl{
		db:                    db,
		accountProviderClient: accountProviderClient,
	}
}
