import { tmpdir } from 'node:os';
import path from 'node:path';

const importMsalNodeExtensions = async (): Promise<
  typeof import('@azure/msal-node-extensions')
> => {
  try {
    return await import('@azure/msal-node-extensions');
  } catch (cause) {
    throw new Error(
      'Failed to load @azure/msal-node-extensions. ' +
        'Token cache persistence requires a native module (keytar/libsecret) that is only ' +
        'available in interactive desktop environments. Install the optional dependency or ' +
        'use a non-caching auth mode.',
      { cause },
    );
  }
};

/**
 * Resolves the directory path for storing the authentication cache.
 *
 * Uses the user's root directory if available, otherwise falls back to the OS temp directory.
 *
 * @returns The resolved cache directory path as a string.
 */
const resolveCachePath = async (): Promise<string> => {
  const { Environment } = await importMsalNodeExtensions();
  return Environment?.getUserRootDirectory() ?? tmpdir();
};

/**
 * Resolves the file path for the authentication cache based on tenant and client IDs.
 *
 * @param tenantId - The Azure AD tenant ID.
 * @param clientId - The Azure AD client/application ID.
 * @returns The full file path for the cache file.
 */
const resolveCacheFilePath = async (tenantId: string, clientId: string): Promise<string> => {
  return path.join(await resolveCachePath(), `.token-cache-${tenantId}_${clientId}`);
};

/**
 * Creates a persistence cache for storing authentication data securely on disk.
 *
 * The cache is encrypted and scoped to the current user for security. It is uniquely identified
 * by the provided tenant and client IDs, and is associated with the 'fusion-framework' service.
 *
 * Requires `@azure/msal-node-extensions` to be installed (optional dependency).
 * Only available in interactive desktop environments with a system keychain.
 *
 * @param tenantId - The Azure AD tenant ID used to identify the cache.
 * @param clientId - The Azure AD client/application ID used to identify the cache.
 * @returns A promise that resolves to the created persistence cache instance.
 */
export const createPersistenceCache = async (tenantId: string, clientId: string) => {
  const { DataProtectionScope, PersistenceCreator } = await importMsalNodeExtensions();
  return PersistenceCreator.createPersistence({
    cachePath: await resolveCacheFilePath(tenantId, clientId),
    serviceName: 'fusion-framework',
    accountName: [tenantId, clientId].join('_'),
    dataProtectionScope: DataProtectionScope.CurrentUser,
  });
};

/**
 * Clears the persistence cache for a specific tenant and client.
 *
 * Deletes the cache file and all associated authentication data for the given tenant and client IDs.
 *
 * @param tenantId - The Azure AD tenant ID.
 * @param clientId - The Azure AD client/application ID.
 * @returns A promise that resolves when the cache has been successfully cleared.
 */
// Deliberately co-located with `createPersistenceCache` above, which it wraps
// fusion-lint-disable-next-line single-export-per-file
export const clearPersistenceCache = async (tenantId: string, clientId: string): Promise<void> => {
  const cache = await createPersistenceCache(tenantId, clientId);
  await cache.delete();
};

/**
 * Creates a `PersistenceCachePlugin` instance for use with MSAL, using the provided tenant and client IDs.
 *
 * This plugin enables MSAL to use the secure persistence cache for token storage.
 *
 * @param tenantId - The Azure AD tenant ID.
 * @param clientId - The Azure AD client/application ID.
 * @returns A promise that resolves to an instance of `PersistenceCachePlugin`.
 */
// Deliberately co-located with `createPersistenceCache` above, which it wraps
// fusion-lint-disable-next-line single-export-per-file
export const createPersistenceCachePlugin = async (tenantId: string, clientId: string) => {
  const { PersistenceCachePlugin } = await importMsalNodeExtensions();
  return new PersistenceCachePlugin(await createPersistenceCache(tenantId, clientId));
};
