class ClientDAO {

  public void sawClient(Client client, @Nullable Principal principal) {
    Instant now = Instant.now();

    Instant lastSeen = Optional.ofNullable(client.getLastSeen())
        .map(ls -> Instant.ofEpochSecond(ls.toEpochSecond()))
        .orElse(EPOCH);

    final Instant expiration;
    if (principal instanceof CertificatePrincipal) {
      expiration = ((CertificatePrincipal) principal).getCertificateExpiration();
    } else {
      expiration = EPOCH;
    }

    // Only update last seen if it's been more than `lastSeenThreshold` seconds
    // this way we can have less granularity on lastSeen and save DB writes
    if (now.isAfter(lastSeen.plus(LAST_SEEN_THRESHOLD))) {
      dslContext.transaction(configuration -> {
        Param<Long> lastSeenValue = DSL.val(now.getEpochSecond(), CLIENTS.LASTSEEN);
        Param<Long> expirationValue = DSL.val(expiration.getEpochSecond(), CLIENTS.EXPIRATION);

        DSL.using(configuration)
            .update(CLIENTS)
            .set(CLIENTS.LASTSEEN,
                when(CLIENTS.LASTSEEN.isNull(), lastSeenValue)
                    .otherwise(greatest(CLIENTS.LASTSEEN, lastSeenValue)))
            .set(CLIENTS.EXPIRATION, expirationValue)
            .where(CLIENTS.ID.eq(client.getId()))
            .execute();
      });
    }
  }

  private  ClientDAO(DSLContext dslContext, ClientMapper clientMapper,
      RowHmacGenerator rowHmacGenerator);

}