package token

import (
	"context"
	"time"

	"github.com/google/uuid"
	"github.com/warmbly/warmbly/internal/errx"
	"github.com/warmbly/warmbly/internal/models"
)

func (s *tokenService) GetSession(ctx context.Context, sessionID uuid.UUID) (*models.Session, *errx.Error) {
	sess, err := s.getSession(ctx, sessionID)
	if err != nil {
		return nil, err
	}

	if sess != nil {
		return sess, nil
	}

	sess, err = s.tokenRepository.GetSession(ctx, sessionID)
	if err != nil {
		return nil, err
	}

	if err := s.saveSession(ctx, sess, SessionTTL); err != nil {
		return nil, err
	}
	return sess, nil
}

func sameTokenIssueTime(a, b time.Time) bool {
	// JWT issued-at precision can differ from DB timestamp precision.
	// Compare at second precision to avoid false mismatches.
	return a.UTC().Truncate(time.Second).Equal(b.UTC().Truncate(time.Second))
}

func (s *tokenService) ValidateAccessToken(ctx context.Context, accessToken string) (*models.Session, *errx.Error) {
	t, err := s.VerifyToken(accessToken)
	if err != nil {
		return nil, err
	}

	if t.ExpiresAt.Before(time.Now()) {
		return nil, errx.ErrToken
	}

	session, err := s.GetSession(ctx, t.SessionID)
	if err != nil {
		return nil, err
	}

	// A revoked session is dead immediately. Revocation busts the Redis cache,
	// so the next read here re-loads the row with revoked_at set and the token
	// stops working without waiting for the cache TTL or a refresh.
	if session.RevokedAt != nil {
		return nil, errx.ErrToken
	}

	if session.AccessNonce != t.Nonce || !sameTokenIssueTime(session.LastRefreshedAt, t.IssuedAt.Time) {
		return nil, errx.ErrToken
	}

	return session, nil
}
