import type { StorageService, HeadObjectResult, SignedUrlOptions, ObjectBody } from '../storage.service';
import * as fs from 'fs/promises';
import * as path from 'path';
import { createReadStream, createWriteStream } from 'fs';
import type { Readable } from 'stream';
import { pipeline } from 'stream/promises';

export class FilesystemAdapter implements StorageService {
  private basePath: string;

  constructor(basePath: string = '/app/data/documents') {
    this.basePath = basePath;
  }

  async putObject(
    _bucket: string,
    key: string,
    body: ObjectBody,
    contentType?: string,
    metadata?: Record<string, string>
  ): Promise<void> {
    const filePath = path.join(this.basePath, key);
    await fs.mkdir(path.dirname(filePath), { recursive: true });

    if (Buffer.isBuffer(body) || typeof body === 'string') {
      // For Buffer or string, write directly
      await fs.writeFile(filePath, body);
    } else {
      // For Readable stream, pipe to file
      const writeStream = createWriteStream(filePath);
      await pipeline(body as Readable, writeStream);
    }

    // Store metadata in a sidecar file if provided
    if (metadata || contentType) {
      const metaPath = `${filePath}.meta.json`;
      const meta: Record<string, string> = { ...(metadata || {}) };
      if (contentType) {
        meta['content-type'] = contentType;
      }
      await fs.writeFile(metaPath, JSON.stringify(meta, null, 2));
    }
  }

  async getObjectStream(_bucket: string, key: string): Promise<Readable> {
    const filePath = path.join(this.basePath, key);
    return createReadStream(filePath);
  }

  async deleteObject(_bucket: string, key: string): Promise<void> {
    const filePath = path.join(this.basePath, key);
    await fs.unlink(filePath);

    // Also delete metadata file if it exists
    const metaPath = `${filePath}.meta.json`;
    try {
      await fs.unlink(metaPath);
    } catch {
      // Ignore if metadata file doesn't exist
    }
  }

  async headObject(_bucket: string, key: string): Promise<HeadObjectResult> {
    const filePath = path.join(this.basePath, key);
    const stats = await fs.stat(filePath);

    // Try to read metadata file
    let contentType: string | undefined;
    let metadata: Record<string, string> | undefined;
    const metaPath = `${filePath}.meta.json`;
    try {
      const metaContent = await fs.readFile(metaPath, 'utf-8');
      const meta = JSON.parse(metaContent);
      contentType = meta['content-type'] || meta['Content-Type'];
      metadata = meta;
    } catch {
      // Ignore if metadata file doesn't exist
    }

    return {
      size: stats.size,
      lastModified: stats.mtime,
      contentType,
      metadata,
    };
  }

  async getSignedUrl(_bucket: string, key: string, _options?: SignedUrlOptions): Promise<string> {
    // For filesystem storage, we don't use signed URLs
    // The documents controller will handle serving the file directly
    return `/api/documents/download/${key}`;
  }
}
