package auth

import (
	"context"

	"gateway/internal/library/zitadel"
	"gateway/internal/svc"
	"gateway/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type LoginSocialCallbackLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewLoginSocialCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginSocialCallbackLogic {
	return &LoginSocialCallbackLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *LoginSocialCallbackLogic) LoginSocialCallback(req *types.LoginSocialCallbackReq) (*types.LoginData, error) {
	if err := requireLoginDeps(l.svcCtx); err != nil {
		return nil, err
	}
	if l.svcCtx.AuthLoginSession == nil {
		return nil, errb.SysNotImplemented("login session not configured")
	}

	sessionID, err := parseLoginOAuthState(req.State)
	if err != nil {
		return nil, err
	}

	session, err := l.svcCtx.AuthLoginSession.Get(l.ctx, sessionID)
	if err != nil {
		return nil, err
	}
	defer func() {
		if delErr := l.svcCtx.AuthLoginSession.Delete(l.ctx, sessionID); delErr != nil {
			logx.WithContext(l.ctx).Errorf("login social callback: delete session: %v", delErr)
		}
	}()

	tok, err := l.svcCtx.Zitadel.ExchangeAuthorizationCode(l.ctx, req.Code, session.RedirectURI)
	if err != nil {
		return nil, wrapZitadelErr(err)
	}

	var claims *zitadel.IDTokenClaims
	if tok.IDToken != "" {
		claims, err = l.svcCtx.Zitadel.VerifyIDToken(l.ctx, tok.IDToken)
	} else {
		claims, err = zitadelIdentityFromToken(l.ctx, l.svcCtx.Zitadel, tok)
	}
	if err != nil {
		return nil, wrapZitadelErr(err)
	}

	trustSocial := l.svcCtx.Config.Member.Defaults().Registration.TrustSocialEmailVerified
	if err := federatedEmailAllowed(claims, session.Provider, trustSocial); err != nil {
		return nil, err
	}

	member, err := resolveMemberForFederatedLogin(l.ctx, l.svcCtx, session.TenantID, claims, session.Provider)
	if err != nil {
		return nil, err
	}

	if member.TOTPEnrolled {
		return beginLoginMFA(l.ctx, l.svcCtx, session.TenantID, session.TenantSlug, member.UID)
	}

	tokens, err := issueAuthToken(l.ctx, l.svcCtx, session.TenantID, member.UID)
	if err != nil {
		return nil, err
	}
	return loginDataFromTokens(tokens), nil
}
