// Path: library/src/main/java/rxreddit/model/AccessToken.java
// public abstract class AccessToken {
// 
//     private final long createdMs = System.currentTimeMillis();
//     @SerializedName("access_token")
//     String token;
//     @SerializedName("token_type")
//     String tokenType;
//     @SerializedName("expires_in")
//     long secondsToExpiration;
//     @SerializedName("scope")
//     String scope;
//     @SerializedName("refresh_token")
//     String refreshToken;
//     private long expirationUtcMs;
// 
//     public String getToken() {
//         return token;
//     }
// 
//     public void setToken(String token) {
//         this.token = token;
//     }
// 
//     public String getTokenType() {
//         return tokenType;
//     }
// 
//     public void setTokenType(String tokenType) {
//         this.tokenType = tokenType;
//     }
// 
//     public long getExpirationMs() {
//         if (expirationUtcMs == 0) {
//             expirationUtcMs = secondsToExpiration * 1000 + createdMs;
//         }
//         return expirationUtcMs;
//     }
// 
//     public void setExpiration(long expiration) {
//         this.expirationUtcMs = expiration;
//     }
// 
//     public String getScope() {
//         return scope;
//     }
// 
//     public void setScope(String scope) {
//         this.scope = scope;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// 
//     public void setRefreshToken(String refreshToken) {
//         this.refreshToken = refreshToken;
//     }
// 
//     public long secondsUntilExpiration() {
//         final long secondsUntilExpiration = (getExpirationMs() - System.currentTimeMillis()) / 1000;
//         return Math.max(0, secondsUntilExpiration);
//     }
// 
//     public abstract boolean isUserAccessToken();
// }
// 
// Path: library/src/main/java/rxreddit/model/ApplicationAccessToken.java
// public class ApplicationAccessToken extends AccessToken {
// 
//     @Override
//     public boolean isUserAccessToken() {
//         return false;
//     }
// }
// 
// Path: library/src/main/java/rxreddit/model/UserAccessToken.java
// public class UserAccessToken extends AccessToken {
// 
//     @Override
//     public boolean isUserAccessToken() {
//         return true;
//     }
// 
//     @Override
//     public String toString() {
//         return String.format(
//                 "Access Token: %s - Expires: %s",
//                 (isUserAccessToken() ? "User" : "Application"),
//                 new Date(getExpirationMs())
//         );
//     }
// }
// 
// Path: library/src/main/java/rxreddit/util/RxRedditUtil.java
// public class RxRedditUtil {
// 
//     /**
//      * Returns a string formed from a random UUID
//      */
//     public static String getRandomString() {
//         return UUID.randomUUID().toString();
//     }
// 
//     /**
//      * Generates a proper user agent string from the provided parameters
//      *
//      * @param platform    Platform for which application is being developed
//      * @param pkgName     Package name for the application
//      * @param versionName Version of the application
//      * @param username    reddit username of the application developer
//      * @return Formatted user agent string
//      */
//     public static String getUserAgent(
//             String platform, String pkgName, String versionName, String username) {
//         return String.format("%s:%s:%s (by /u/%s)", platform, pkgName, versionName, username);
//     }
// 
//     public static Map<String, String> getQueryParametersFromUrl(String url)
//             throws IllegalArgumentException {
//         if (url == null) throw new IllegalArgumentException("url == null");
//         URI uri = URI.create(url);
//         String query = uri.getQuery();
//         if (query == null) return Collections.emptyMap();
//         String[] params = query.split("&");
//         Map<String, String> paramMap = new HashMap<>();
//         int mid;
//         for (String param : params) {
//             mid = param.indexOf('=');
//             if (mid != -1) paramMap.put(param.substring(0, mid), param.substring(mid + 1));
//         }
//         return paramMap;
//     }
// 
//     public static <T> Observable<T> responseToBody(Response<T> response) {
//         if (!response.isSuccessful()) {
//             return Observable.error(new HttpException(response));
//         }
//         return Observable.just(response.body());
//     }
// 
//     public static <T> Observable<Response<T>> checkResponse(Response<T> response) {
//         if (!response.isSuccessful()) {
//             return Observable.error(new HttpException(response));
//         }
//         return Observable.just(response);
//     }
// }


import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Observable;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.HttpException;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rxreddit.model.AccessToken;
import rxreddit.model.ApplicationAccessToken;
import rxreddit.model.UserAccessToken;
import rxreddit.util.RxRedditUtil;

    private UserAccessToken getUserAccessToken() {
        if (userAccessToken == null) {
            userAccessToken = accessTokenManager.getUserAccessToken();
        }
        return userAccessToken;
    }

    private ApplicationAccessToken getApplicationAccessToken() {
        if (applicationAccessToken == null) {
            applicationAccessToken = accessTokenManager.getApplicationAccessToken();
        }
        return applicationAccessToken;
    }

    private Observable<ApplicationAccessToken> refreshApplicationAccessToken() {
        return Observable.defer(() -> {
            ApplicationAccessToken token = getApplicationAccessToken();
            if (token != null && token.secondsUntilExpiration() > EXPIRATION_THRESHOLD) {
                return Observable.just(token);
            } else {
                String grantType = "https://oauth.reddit.com/grants/installed_client";
                return authService.getApplicationAuthToken(grantType, deviceId)
                        .flatMap(RxRedditUtil::responseToBody)
                        .doOnNext(this::saveApplicationAccessToken);
            }
        });
    }

    @Override
// Path: library/src/main/java/rxreddit/model/AccessToken.java
// public abstract class AccessToken {
// 
//     private final long createdMs = System.currentTimeMillis();
//     @SerializedName("access_token")
//     String token;
//     @SerializedName("token_type")
//     String tokenType;
//     @SerializedName("expires_in")
//     long secondsToExpiration;
//     @SerializedName("scope")
//     String scope;
//     @SerializedName("refresh_token")
//     String refreshToken;
//     private long expirationUtcMs;
// 
//     public String getToken() {
//         return token;
//     }
// 
//     public void setToken(String token) {
//         this.token = token;
//     }
// 
//     public String getTokenType() {
//         return tokenType;
//     }
// 
//     public void setTokenType(String tokenType) {
//         this.tokenType = tokenType;
//     }
// 
//     public long getExpirationMs() {
//         if (expirationUtcMs == 0) {
//             expirationUtcMs = secondsToExpiration * 1000 + createdMs;
//         }
//         return expirationUtcMs;
//     }
// 
//     public void setExpiration(long expiration) {
//         this.expirationUtcMs = expiration;
//     }
// 
//     public String getScope() {
//         return scope;
//     }
// 
//     public void setScope(String scope) {
//         this.scope = scope;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// 
//     public void setRefreshToken(String refreshToken) {
//         this.refreshToken = refreshToken;
//     }
// 
//     public long secondsUntilExpiration() {
//         final long secondsUntilExpiration = (getExpirationMs() - System.currentTimeMillis()) / 1000;
//         return Math.max(0, secondsUntilExpiration);
//     }
// 
//     public abstract boolean isUserAccessToken();
// }
// 
// Path: library/src/main/java/rxreddit/model/ApplicationAccessToken.java
// public class ApplicationAccessToken extends AccessToken {
// 
//     @Override
//     public boolean isUserAccessToken() {
//         return false;
//     }
// }
// 
// Path: library/src/main/java/rxreddit/model/UserAccessToken.java
// public class UserAccessToken extends AccessToken {
// 
//     @Override
//     public boolean isUserAccessToken() {
//         return true;
//     }
// 
//     @Override
//     public String toString() {
//         return String.format(
//                 "Access Token: %s - Expires: %s",
//                 (isUserAccessToken() ? "User" : "Application"),
//                 new Date(getExpirationMs())
//         );
//     }
// }
// 
// Path: library/src/main/java/rxreddit/util/RxRedditUtil.java
// public class RxRedditUtil {
// 
//     /**
//      * Returns a string formed from a random UUID
//      */
//     public static String getRandomString() {
//         return UUID.randomUUID().toString();
//     }
// 
//     /**
//      * Generates a proper user agent string from the provided parameters
//      *
//      * @param platform    Platform for which application is being developed
//      * @param pkgName     Package name for the application
//      * @param versionName Version of the application
//      * @param username    reddit username of the application developer
//      * @return Formatted user agent string
//      */
//     public static String getUserAgent(
//             String platform, String pkgName, String versionName, String username) {
//         return String.format("%s:%s:%s (by /u/%s)", platform, pkgName, versionName, username);
//     }
// 
//     public static Map<String, String> getQueryParametersFromUrl(String url)
//             throws IllegalArgumentException {
//         if (url == null) throw new IllegalArgumentException("url == null");
//         URI uri = URI.create(url);
//         String query = uri.getQuery();
//         if (query == null) return Collections.emptyMap();
//         String[] params = query.split("&");
//         Map<String, String> paramMap = new HashMap<>();
//         int mid;
//         for (String param : params) {
//             mid = param.indexOf('=');
//             if (mid != -1) paramMap.put(param.substring(0, mid), param.substring(mid + 1));
//         }
//         return paramMap;
//     }
// 
//     public static <T> Observable<T> responseToBody(Response<T> response) {
//         if (!response.isSuccessful()) {
//             return Observable.error(new HttpException(response));
//         }
//         return Observable.just(response.body());
//     }
// 
//     public static <T> Observable<Response<T>> checkResponse(Response<T> response) {
//         if (!response.isSuccessful()) {
//             return Observable.error(new HttpException(response));
//         }
//         return Observable.just(response);
//     }
// }


// Path: library/src/main/java/rxreddit/api/RedditAuthService.java
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Observable;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.HttpException;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rxreddit.model.AccessToken;
import rxreddit.model.ApplicationAccessToken;
import rxreddit.model.UserAccessToken;
import rxreddit.util.RxRedditUtil;


    private UserAccessToken getUserAccessToken() {
        if (userAccessToken == null) {
            userAccessToken = accessTokenManager.getUserAccessToken();
        }
        return userAccessToken;
    }

    private ApplicationAccessToken getApplicationAccessToken() {
        if (applicationAccessToken == null) {
            applicationAccessToken = accessTokenManager.getApplicationAccessToken();
        }
        return applicationAccessToken;
    }

    private Observable<ApplicationAccessToken> refreshApplicationAccessToken() {
        return Observable.defer(() -> {
            ApplicationAccessToken token = getApplicationAccessToken();
            if (token != null && token.secondsUntilExpiration() > EXPIRATION_THRESHOLD) {
                return Observable.just(token);
            } else {
                String grantType = "https://oauth.reddit.com/grants/installed_client";
                return authService.getApplicationAuthToken(grantType, deviceId)
                        .flatMap(RxRedditUtil::responseToBody)
                        .doOnNext(this::saveApplicationAccessToken);
            }
        });
    }

    @Override
    public Observable<AccessToken> refreshAccessToken() {