import { BadRequestException, Injectable } from '@nestjs/common';
import { promises as fs } from 'fs';
import * as path from 'path';
import { createLogger } from '@core/observability/logging/config/winston.config';
import type { StorageAdapter } from './storage.types';

const logger = createLogger('LocalStorageAdapter');

const DEFAULT_ROOT = process.env.FFAI_STORAGE_LOCAL_ROOT || '/tmp/ffai-agent-storage';

@Injectable()
export class LocalStorageAdapter implements StorageAdapter {
  readonly kind = 'LOCAL' as const;

  isConfigured(_config: Record<string, unknown>): boolean {
    return true; // 本地永远可用
  }

  private resolveRoot(config: Record<string, unknown>): string {
    return (config.rootPath as string) || DEFAULT_ROOT;
  }

  // LLM 或客户端可控的相对路径必须收敛在 root 内；阻断 ../ 路径遍历写出 root
  private resolveSafe(root: string, rel: string): string {
    const rootAbs = path.resolve(root);
    const full = path.resolve(rootAbs, rel);
    if (full !== rootAbs && !full.startsWith(rootAbs + path.sep)) {
      throw new BadRequestException(`path escapes storage root: ${rel}`);
    }
    return full;
  }

  async upload(args: { config: Record<string, unknown>; path: string; content: Buffer }) {
    const root = this.resolveRoot(args.config);
    const full = this.resolveSafe(root, args.path);
    await fs.mkdir(path.dirname(full), { recursive: true });
    await fs.writeFile(full, args.content);
    logger.log(`uploaded ${args.path} (${args.content.length} bytes) → ${full}`);
    return { externalId: undefined };
  }

  async download(args: { config: Record<string, unknown>; path: string }): Promise<Buffer> {
    const root = this.resolveRoot(args.config);
    const full = this.resolveSafe(root, args.path);
    return fs.readFile(full);
  }
}
