/**
 * Local Storage Provider for LocalGreenChain
 * Agent 3: File Upload & Storage System
 *
 * Filesystem-based storage for development and testing
 */

import { promises as fs } from 'fs';
import path from 'path';
import crypto from 'crypto';
import { StorageConfig, StorageProviderInterface } from '../types';

export class LocalStorageProvider implements StorageProviderInterface {
  private basePath: string;
  private publicUrl: string;

  constructor(config: StorageConfig) {
    this.basePath = config.localPath || './uploads';
    this.publicUrl = config.publicUrl || 'http://localhost:3001';
  }

  private getFilePath(key: string): string {
    return path.join(this.basePath, key);
  }

  private async ensureDirectory(filePath: string): Promise<void> {
    const dir = path.dirname(filePath);
    await fs.mkdir(dir, { recursive: true });
  }

  async upload(key: string, buffer: Buffer, contentType: string): Promise<string> {
    const filePath = this.getFilePath(key);
    await this.ensureDirectory(filePath);

    await fs.writeFile(filePath, buffer);

    // Write metadata file
    const metadataPath = `${filePath}.meta.json`;
    await fs.writeFile(
      metadataPath,
      JSON.stringify({
        contentType,
        size: buffer.length,
        uploadedAt: new Date().toISOString(),
      })
    );

    return this.getPublicUrl(key);
  }

  async delete(key: string): Promise<boolean> {
    try {
      const filePath = this.getFilePath(key);
      await fs.unlink(filePath);

      // Also delete metadata file if exists
      try {
        await fs.unlink(`${filePath}.meta.json`);
      } catch {
        // Metadata file may not exist
      }

      return true;
    } catch (error) {
      console.error('Error deleting local file:', error);
      return false;
    }
  }

  async getSignedUrl(key: string, expiresIn = 3600): Promise<string> {
    // For local storage, we generate a signed URL using HMAC
    const expires = Date.now() + expiresIn * 1000;
    const secret = process.env.LOCAL_STORAGE_SECRET || 'development-secret';
    const signature = crypto
      .createHmac('sha256', secret)
      .update(`${key}:${expires}`)
      .digest('hex')
      .substring(0, 16);

    return `${this.publicUrl}/api/upload/${encodeURIComponent(key)}?expires=${expires}&sig=${signature}`;
  }

  async getPresignedUploadUrl(key: string, contentType: string, expiresIn = 3600): Promise<string> {
    // For local storage, return an API endpoint for upload
    const expires = Date.now() + expiresIn * 1000;
    const secret = process.env.LOCAL_STORAGE_SECRET || 'development-secret';
    const signature = crypto
      .createHmac('sha256', secret)
      .update(`upload:${key}:${contentType}:${expires}`)
      .digest('hex')
      .substring(0, 16);

    return `${this.publicUrl}/api/upload/presigned?key=${encodeURIComponent(key)}&contentType=${encodeURIComponent(contentType)}&expires=${expires}&sig=${signature}`;
  }

  async exists(key: string): Promise<boolean> {
    try {
      await fs.access(this.getFilePath(key));
      return true;
    } catch {
      return false;
    }
  }

  getPublicUrl(key: string): string {
    return `${this.publicUrl}/uploads/${key}`;
  }

  /**
   * Verify a signed URL signature
   */
  static verifySignature(
    key: string,
    expires: number,
    signature: string,
    prefix = ''
  ): boolean {
    if (Date.now() > expires) {
      return false;
    }

    const secret = process.env.LOCAL_STORAGE_SECRET || 'development-secret';
    const expectedSignature = crypto
      .createHmac('sha256', secret)
      .update(`${prefix}${key}:${expires}`)
      .digest('hex')
      .substring(0, 16);

    return signature === expectedSignature;
  }
}
