import path from 'node:path';

type ExistsSync = (candidate: string) => boolean;

type ConfigPathOptions = {
  platform?: NodeJS.Platform;
  appDataDir?: string;
  xdgConfigHome?: string;
  homeDir: string;
  existsSync: ExistsSync;
  appNames?: readonly string[];
  defaultAppName?: string;
};

const DEFAULT_APP_NAMES = ['SubMiner'] as const;
const DEFAULT_FILE_NAMES = ['config.jsonc', 'config.json'] as const;

function getPlatformPath(platform: NodeJS.Platform): typeof path.posix | typeof path.win32 {
  return platform === 'win32' ? path.win32 : path.posix;
}

export function resolveConfigBaseDirs(
  xdgConfigHome: string | undefined,
  homeDir: string,
  platform: NodeJS.Platform = process.platform,
  appDataDir?: string,
): string[] {
  const platformPath = getPlatformPath(platform);
  if (platform === 'win32') {
    const roamingBaseDir = platformPath.join(homeDir, 'AppData', 'Roaming');
    const primaryBaseDir = appDataDir?.trim() || roamingBaseDir;
    return Array.from(new Set([primaryBaseDir, roamingBaseDir]));
  }

  const fallbackBaseDir = platformPath.join(homeDir, '.config');
  const primaryBaseDir = xdgConfigHome?.trim() || fallbackBaseDir;
  return Array.from(new Set([primaryBaseDir, fallbackBaseDir]));
}

function getAppNames(options: ConfigPathOptions): readonly string[] {
  return options.appNames ?? DEFAULT_APP_NAMES;
}

function getDefaultAppName(options: ConfigPathOptions): string {
  return options.defaultAppName ?? DEFAULT_APP_NAMES[0];
}

export function resolveConfigDir(options: ConfigPathOptions): string {
  const platform = options.platform ?? process.platform;
  const platformPath = getPlatformPath(platform);
  const baseDirs = resolveConfigBaseDirs(
    options.xdgConfigHome,
    options.homeDir,
    platform,
    options.appDataDir,
  );
  const appNames = getAppNames(options);

  for (const baseDir of baseDirs) {
    for (const appName of appNames) {
      const dir = platformPath.join(baseDir, appName);
      for (const fileName of DEFAULT_FILE_NAMES) {
        if (options.existsSync(platformPath.join(dir, fileName))) {
          return dir;
        }
      }
    }
  }

  for (const baseDir of baseDirs) {
    for (const appName of appNames) {
      const dir = platformPath.join(baseDir, appName);
      if (options.existsSync(dir)) {
        return dir;
      }
    }
  }

  return platformPath.join(baseDirs[0]!, getDefaultAppName(options));
}

export function resolveConfigFilePath(options: ConfigPathOptions): string {
  const platform = options.platform ?? process.platform;
  const platformPath = getPlatformPath(platform);
  const baseDirs = resolveConfigBaseDirs(
    options.xdgConfigHome,
    options.homeDir,
    platform,
    options.appDataDir,
  );
  const appNames = getAppNames(options);

  for (const baseDir of baseDirs) {
    for (const appName of appNames) {
      for (const fileName of DEFAULT_FILE_NAMES) {
        const candidate = platformPath.join(baseDir, appName, fileName);
        if (options.existsSync(candidate)) {
          return candidate;
        }
      }
    }
  }

  return platformPath.join(baseDirs[0]!, getDefaultAppName(options), DEFAULT_FILE_NAMES[0]!);
}
