import { createLogger } from '../logger.js';
import { AuthError } from '../errors.js';
import { discoverAndRefreshToken, createReauthError } from './oauth-utils.js';
const logger = createLogger('oauth-token-manager');
const DEFAULT_TOKEN_EXPIRY_SECONDS = 3600;
const EXPIRY_BUFFER_SECONDS = 60;
export class OAuthTokenManager {
    serverUrl;
    profileName;
    clientId;
    refreshToken;
    accessToken = null;
    accessTokenExpiresAt = null;
    onTokenRefresh;
    onBeforeRefresh;
    constructor(options) {
        this.serverUrl = options.serverUrl;
        this.profileName = options.profileName;
        this.clientId = options.clientId;
        this.refreshToken = options.refreshToken;
        this.accessToken = options.accessToken ?? null;
        this.accessTokenExpiresAt = options.accessTokenExpiresAt ?? null;
        if (options.onTokenRefresh) {
            this.onTokenRefresh = options.onTokenRefresh;
        }
        if (options.onBeforeRefresh) {
            this.onBeforeRefresh = options.onBeforeRefresh;
        }
    }
    isAccessTokenExpired() {
        if (!this.accessToken || !this.accessTokenExpiresAt) {
            return true;
        }
        return Date.now() / 1000 > this.accessTokenExpiresAt - EXPIRY_BUFFER_SECONDS;
    }
    getSecondsUntilExpiry() {
        if (!this.accessToken || !this.accessTokenExpiresAt) {
            return 0;
        }
        const secondsUntil = this.accessTokenExpiresAt - EXPIRY_BUFFER_SECONDS - Math.floor(Date.now() / 1000);
        return Math.max(0, secondsUntil);
    }
    async refreshAccessToken() {
        if (this.onBeforeRefresh) {
            logger.debug('Reloading tokens from storage before refresh...');
            const latestTokens = await this.onBeforeRefresh();
            if (latestTokens) {
                if (latestTokens.refreshToken && latestTokens.refreshToken !== this.refreshToken) {
                    logger.debug('Found newer refresh token in storage (another process rotated it)');
                    this.refreshToken = latestTokens.refreshToken;
                }
                if (latestTokens.accessToken && latestTokens.accessTokenExpiresAt) {
                    const nowSeconds = Math.floor(Date.now() / 1000);
                    if (latestTokens.accessTokenExpiresAt > nowSeconds + EXPIRY_BUFFER_SECONDS) {
                        logger.debug('Found valid access token in storage, using it instead of refreshing');
                        this.accessToken = latestTokens.accessToken;
                        this.accessTokenExpiresAt = latestTokens.accessTokenExpiresAt;
                        return {
                            access_token: this.accessToken,
                            token_type: 'Bearer',
                        };
                    }
                }
            }
        }
        if (!this.refreshToken) {
            throw createReauthError(this.serverUrl, this.profileName, `No refresh token available for profile ${this.profileName}`);
        }
        logger.debug(`Refreshing access token for profile: ${this.profileName}`);
        try {
            const tokenResponse = await discoverAndRefreshToken(this.serverUrl, this.refreshToken, this.clientId);
            this.accessToken = tokenResponse.access_token;
            const expiresIn = tokenResponse.expires_in ?? DEFAULT_TOKEN_EXPIRY_SECONDS;
            this.accessTokenExpiresAt = Math.floor(Date.now() / 1000) + expiresIn;
            if (tokenResponse.refresh_token) {
                this.refreshToken = tokenResponse.refresh_token;
                logger.debug('Received new refresh token (token rotation)');
            }
            logger.debug(`Access token refreshed successfully for profile: ${this.profileName}`);
            if (this.onTokenRefresh) {
                await this.onTokenRefresh(tokenResponse);
            }
            return tokenResponse;
        }
        catch (error) {
            if (error instanceof AuthError) {
                throw createReauthError(this.serverUrl, this.profileName, error.message);
            }
            logger.error(`Token refresh error: ${error.message}`);
            throw createReauthError(this.serverUrl, this.profileName, `Failed to refresh token: ${error.message}`);
        }
    }
    async getValidAccessToken() {
        if (this.isAccessTokenExpired()) {
            await this.refreshAccessToken();
        }
        if (!this.accessToken) {
            throw new AuthError('No access token available after refresh');
        }
        return this.accessToken;
    }
}
//# sourceMappingURL=oauth-token-manager.js.map