/**
 * FileSystemMediaStorageAdapter
 * 
 * Concrete adapter for storing media files on the filesystem.
 * Implements the MediaStoragePort interface.
 */

import { MediaStoragePort, UploadOptions, UploadResult } from '@core/media/application/ports/MediaStoragePort';
import * as fs from 'fs/promises';
import * as path from 'path';

/**
 * Configuration for FileSystemMediaStorageAdapter
 */
export interface FileSystemMediaStorageConfig {
  /**
   * Base directory for storing media files
   * @default '/data/media'
   */
  baseDir?: string;
}

/**
 * FileSystemMediaStorageAdapter
 * 
 * Stores media files in a local filesystem directory.
 * Uses deterministic storage keys based on mediaId.
 */
export class FileSystemMediaStorageAdapter implements MediaStoragePort {
  private readonly baseDir: string;

  constructor(config: FileSystemMediaStorageConfig = {}) {
    this.baseDir = config.baseDir || '/data/media';
  }

  /**
   * Upload a media file to the filesystem
   * 
   * @param buffer File buffer
   * @param options Upload options
   * @returns Upload result with storage key
   */
  async uploadMedia(buffer: Buffer, options: UploadOptions): Promise<UploadResult> {
    try {
      // Validate content type
      const allowedTypes = ['image/png', 'image/jpeg', 'image/svg+xml', 'image/gif'];
      if (!allowedTypes.includes(options.mimeType)) {
        return {
          success: false,
          errorMessage: `Content type ${options.mimeType} is not allowed`,
        };
      }

      // Generate deterministic storage key
      const mediaId = this.generateMediaId(options.filename);
      const storageKey = `uploaded/${mediaId}`;
      const filePath = path.join(this.baseDir, storageKey);

      // Ensure directory exists
      await fs.mkdir(path.dirname(filePath), { recursive: true });

      // Write file
      await fs.writeFile(filePath, buffer);

      return {
        success: true,
        filename: options.filename,
        url: storageKey, // Return storage key, not full URL
      };
    } catch (error) {
      return {
        success: false,
        errorMessage: error instanceof Error ? error.message : String(error),
      };
    }
  }

  /**
   * Delete a media file from the filesystem
   * 
   * @param storageKey Storage key (e.g., 'uploaded/media-123')
   */
  async deleteMedia(storageKey: string): Promise<void> {
    try {
      const filePath = path.join(this.baseDir, storageKey);
      await fs.unlink(filePath);
    } catch (error) {
      // Ignore if file doesn't exist
      if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
        return;
      }
      throw error;
    }
  }

  /**
   * Get file bytes as Buffer
   * 
   * @param storageKey Storage key
   * @returns Buffer or null if not found
   */
  async getBytes(storageKey: string): Promise<Buffer | null> {
    try {
      const filePath = path.join(this.baseDir, storageKey);
      return await fs.readFile(filePath);
    } catch (error) {
      return null;
    }
  }

  /**
   * Get file metadata
   * 
   * @param storageKey Storage key
   * @returns File metadata or null if not found
   */
  async getMetadata(storageKey: string): Promise<{ size: number; contentType: string } | null> {
    try {
      const filePath = path.join(this.baseDir, storageKey);
      const stat = await fs.stat(filePath);
      
      // Determine content type from extension
      const ext = path.extname(filePath).toLowerCase();
      const contentType = this.getContentTypeFromExtension(ext);
      
      return {
        size: stat.size,
        contentType,
      };
    } catch (error) {
      return null;
    }
  }

  /**
   * Generate a deterministic media ID from filename
   */
  private generateMediaId(filename: string): string {
    const timestamp = Date.now();
    const cleanFilename = filename.replace(/[^a-zA-Z0-9.-]/g, '_');
    return `media-${timestamp}-${cleanFilename}`;
  }

  /**
   * Get content type from file extension
   */
  private getContentTypeFromExtension(ext: string): string {
    const map: Record<string, string> = {
      '.png': 'image/png',
      '.jpg': 'image/jpeg',
      '.jpeg': 'image/jpeg',
      '.svg': 'image/svg+xml',
      '.gif': 'image/gif',
    };
    return map[ext] || 'application/octet-stream';
  }
}

/**
 * Factory function for creating FileSystemMediaStorageAdapter instances
 */
export function createFileSystemMediaStorage(
  config: FileSystemMediaStorageConfig = {}
): FileSystemMediaStorageAdapter {
  return new FileSystemMediaStorageAdapter(config);
}