import * as plugins from '../../plugins.js';
import { SimpleLocalClient } from '../../core/index.js';
import { FileMapper } from './file.mapper.js';
import type { IFileConfig, IFileSnapshot } from './file.types.js';
import { fileProfile } from './file.types.js';

export class FileClient extends SimpleLocalClient<IFileConfig> {
  constructor(private readonly configArg: IFileConfig) {
    super(fileProfile, configArg);
  }

  public async getSnapshot(forceRefreshArg = false): Promise<IFileSnapshot> {
    if (this.shouldReadLocalFile()) {
      try {
        return FileMapper.toSnapshot({ config: this.configArg, rawData: await this.readLocalFile(), online: true, source: 'manual' });
      } catch (errorArg) {
        return FileMapper.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 FileMapper.toSnapshot({ config: { ...this.configArg, snapshot }, online: snapshot.online, source: snapshot.source, error: snapshot.error });
    }

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

  private shouldReadLocalFile(): 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 readLocalFile(): Promise<Record<string, unknown>> {
    const filePath = this.filePath();
    if (!filePath) {
      throw new Error('File snapshots require config.filePath or config.file_path.');
    }

    const content = await plugins.fs.readFile(filePath, 'utf8');
    const latestEntry = content.trimEnd().split(/\r?\n/).pop()?.trim() ?? '';
    return {
      filePath,
      latestEntry,
      contentBytes: Buffer.byteLength(content),
    };
  }

  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 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);
