import * as plugins from '../../plugins.js';
import { SimpleLocalClient } from '../../core/index.js';
import { FilesizeMapper } from './filesize.mapper.js';
import type { IFilesizeConfig, IFilesizeSnapshot } from './filesize.types.js';
import { filesizeProfile } from './filesize.types.js';

export class FilesizeClient extends SimpleLocalClient<IFilesizeConfig> {
  constructor(private readonly configArg: IFilesizeConfig) {
    super(filesizeProfile, configArg);
  }

  public async getSnapshot(forceRefreshArg = false): Promise<IFilesizeSnapshot> {
    if (this.shouldStatLocalFile()) {
      try {
        return FilesizeMapper.toSnapshot({ config: this.configArg, rawData: await this.readLocalStats(), online: true, source: 'manual' });
      } catch (errorArg) {
        return FilesizeMapper.toSnapshot({ config: this.configArg, online: false, source: 'runtime', error: errorMessage(errorArg) });
      }
    }

    const snapshot = await super.getSnapshot(forceRefreshArg);
    if (snapshot.rawData === undefined && snapshot.entities.length) {
      return FilesizeMapper.toSnapshot({ config: { ...this.configArg, snapshot }, online: snapshot.online, source: snapshot.source, error: snapshot.error });
    }

    return FilesizeMapper.toSnapshot({ config: this.configArg, rawData: snapshot.rawData, online: snapshot.online, source: snapshot.source, error: snapshot.error });
  }

  private shouldStatLocalFile(): boolean {
    return Boolean(this.filePath() && !this.configArg.snapshot && this.configArg.rawData === undefined && this.configArg.rawText === undefined && !this.configArg.entities?.length && !this.configArg.client && !this.configArg.snapshotProvider && !this.configArg.host);
  }

  private async readLocalStats(): Promise<Record<string, unknown>> {
    const filePath = this.filePath();
    if (!filePath) {
      throw new Error('File Size snapshots require config.filePath or config.file_path.');
    }

    const stat = await plugins.fs.stat(filePath);
    if (!stat.isFile()) {
      throw new Error(`File Size snapshots require a file path, not a directory: ${filePath}`);
    }

    return {
      filePath,
      file: roundMegabytes(stat.size),
      bytes: stat.size,
      lastUpdated: stat.mtime.toISOString(),
      created: stat.ctime.toISOString(),
    };
  }

  private filePath(): string | undefined {
    const metadata = this.configArg.metadata;
    return stringValue(this.configArg.filePath) || stringValue(this.configArg.file_path) || stringValue(metadata?.filePath) || stringValue(metadata?.file_path);
  }
}

const roundMegabytes = (bytesArg: number): number => Math.round((bytesArg / 1e6) * 100) / 100;

const stringValue = (valueArg: unknown): string | undefined => typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;

const errorMessage = (errorArg: unknown): string => errorArg instanceof Error ? errorArg.message : String(errorArg);
