import fs from 'fs';
import path from 'path';
import { fetchPublicHttpUrl } from '@/lib/remote-fetch';

class LocalStorage {
  private basePath: string;

  constructor() {
    this.basePath = process.env.LOCAL_STORAGE_DIR
      ? path.resolve(/* turbopackIgnore: true */ process.env.LOCAL_STORAGE_DIR)
      : path.join(/* turbopackIgnore: true */ process.cwd(), 'local-storage');
    this.ensureDirectoryExists(this.basePath);
  }

  private ensureDirectoryExists(dir: string): void {
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
  }

  private normalizeKey(key: string): string {
    const normalized = path.posix.normalize(key.replace(/\\/g, '/')).replace(/^\/+/, '');
    if (!normalized || normalized === '.' || normalized.startsWith('../') || normalized.includes('/../') || normalized.includes('\0')) {
      throw new Error('Invalid storage key');
    }
    return normalized;
  }

  async uploadFile({ fileContent, fileName, contentType }: {
    fileContent: Buffer;
    fileName: string;
    contentType: string;
  }): Promise<string> {
    const key = this.normalizeKey(fileName);
    const filePath = this.getFilePath(key);
    const directory = path.dirname(filePath);
    
    this.ensureDirectoryExists(directory);
    fs.writeFileSync(filePath, fileContent);
    
    return key;
  }

  async uploadFromUrl({ url, timeout }: {
    url: string;
    timeout: number;
  }): Promise<string> {
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), timeout);
      
      const response = await fetchPublicHttpUrl(url, { signal: controller.signal });
      clearTimeout(timeoutId);
      
      if (!response.ok) {
        throw new Error(`Failed to fetch URL: ${response.status}`);
      }
      
      const buffer = await response.arrayBuffer();
      const fileName = `remote/${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${this.getFileExtension(url)}`;
      
      return this.uploadFile({
        fileContent: Buffer.from(buffer),
        fileName,
        contentType: response.headers.get('content-type') || 'application/octet-stream'
      });
    } catch (error) {
      console.error('[LocalStorage] uploadFromUrl error:', error);
      throw error;
    }
  }

  async generatePresignedUrl({ key, expireTime }: {
    key: string;
    expireTime: number;
  }): Promise<string> {
    // In local storage, we just return a local file path
    // In production, this would be a presigned URL
    return `/api/local-storage/${key}`;
  }

  private getFileExtension(url: string): string {
    const match = url.match(/\.([^.]+)$/);
    return match ? match[1] : 'bin';
  }

  getFilePath(key: string): string {
    const normalized = this.normalizeKey(key);
    const filePath = path.resolve(/* turbopackIgnore: true */ this.basePath, normalized);
    const basePath = path.resolve(/* turbopackIgnore: true */ this.basePath);
    if (filePath !== basePath && !filePath.startsWith(`${basePath}${path.sep}`)) {
      throw new Error('Invalid storage key');
    }
    return filePath;
  }

  fileExists(key: string): boolean {
    return fs.existsSync(this.getFilePath(key));
  }

  readFile(key: string): Buffer {
    return fs.readFileSync(this.getFilePath(key));
  }

  getKeyFromPublicUrl(url: string): string | null {
    const marker = '/api/local-storage/';
    const index = url.indexOf(marker);
    if (index === -1) return null;
    return decodeURIComponent(url.slice(index + marker.length).split('?')[0]);
  }

  async copyPublicUrlToFolder(url: string, folder: string): Promise<string> {
    const existingKey = this.getKeyFromPublicUrl(url);
    let buffer: Buffer;
    let ext = this.getFileExtension(url);

    if (existingKey && this.fileExists(existingKey)) {
      buffer = this.readFile(existingKey);
      ext = path.extname(existingKey).replace('.', '') || ext;
    } else if (url.startsWith('http')) {
      const response = await fetchPublicHttpUrl(url);
      if (!response.ok) throw new Error(`Failed to fetch URL: ${response.status}`);
      buffer = Buffer.from(await response.arrayBuffer());
      ext = this.getFileExtension(url);
    } else {
      return url;
    }

    const key = `${folder}/${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext || 'bin'}`;
    const savedKey = await this.uploadFile({
      fileContent: buffer,
      fileName: key,
      contentType: 'application/octet-stream',
    });
    return this.generatePresignedUrl({ key: savedKey, expireTime: 2592000 });
  }
}

export const localStorage = new LocalStorage();
export default LocalStorage;
