import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as path from 'path';
import * as fs from 'fs/promises';
import * as crypto from 'crypto';

/**
 * Storage Service — Raspberry Pi 5 Üretim Ortamı İçin Optimize Edildi
 *
 * Tüm medya dosyaları Raspberry Pi'nin lokal diskinde saklanır.
 * Dosya yapısı:
 *   {basePath}/
 *     ├── {projectId}/
 *     │   ├── scenes/
 *     │   │   ├── scene-001-video.mp4
 *     │   │   ├── scene-001-audio.mp3
 *     │   │   └── scene-002-video.mp4
 *     │   ├── audio/
 *     │   │   ├── narration.mp3
 *     │   │   └── music.mp3
 *     │   ├── images/
 *     │   │   ├── scene-001.png
 *     │   │   └── scene-002.png
 *     │   ├── output/
 *     │   │   ├── final-xxxx.mp4
 *     │   │   └── thumbnail.jpg
 *     │   └── subtitles/
 *     │       └── captions.srt
 *     └── temp/ (otomatik temizlenir)
 */

export interface UploadResult {
  key: string;
  url: string;
  bucket: string;
  sizeBytes: number;
  mimeType: string;
}

export interface StorageConfig {
  provider: 'local';
  basePath: string;
  publicBaseUrl: string;
}

@Injectable()
export class StorageService {
  private readonly logger = new Logger(StorageService.name);
  private readonly config: StorageConfig;

  constructor(private readonly configService: ConfigService) {
    const basePath = this.configService.get<string>('STORAGE_LOCAL_PATH', './data/media');
    const port = this.configService.get<number>('PORT', 3000);
    const cdnUrl = this.configService.get<string>('STORAGE_CDN_URL');

    this.config = {
      provider: 'local',
      basePath: path.resolve(basePath),
      publicBaseUrl: cdnUrl || `http://localhost:${port}/media`,
    };

    this.logger.log(`📦 Storage: lokal depolama — ${this.config.basePath}`);
    this.ensureBaseDir();
  }

  /**
   * Başlangıçta temel dizini oluştur.
   */
  private async ensureBaseDir() {
    try {
      await fs.mkdir(this.config.basePath, { recursive: true });
      await fs.mkdir(path.join(this.config.basePath, 'temp'), { recursive: true });
    } catch (error) {
      this.logger.error(`Temel dizin oluşturulamadı: ${error}`);
    }
  }

  // ── Key Generators ─────────────────────────────────────────────────

  getSceneVideoKey(projectId: string, sceneOrder: number): string {
    return `${projectId}/scenes/scene-${String(sceneOrder).padStart(3, '0')}-video.mp4`;
  }

  getSceneAudioKey(projectId: string, sceneOrder: number): string {
    return `${projectId}/audio/scene-${String(sceneOrder).padStart(3, '0')}-narration.mp3`;
  }

  getSceneImageKey(projectId: string, sceneOrder: number, ext = 'png'): string {
    return `${projectId}/images/scene-${String(sceneOrder).padStart(3, '0')}.${ext}`;
  }

  getFinalVideoKey(projectId: string): string {
    const hash = crypto.randomBytes(4).toString('hex');
    return `${projectId}/output/final-${hash}.mp4`;
  }

  getThumbnailKey(projectId: string): string {
    return `${projectId}/output/thumbnail.jpg`;
  }

  getSubtitleKey(projectId: string): string {
    return `${projectId}/subtitles/captions.srt`;
  }

  getMusicKey(projectId: string): string {
    return `${projectId}/audio/background-music.mp3`;
  }

  getAmbientKey(projectId: string, sceneOrder: number): string {
    return `${projectId}/audio/ambient-${String(sceneOrder).padStart(3, '0')}.mp3`;
  }

  getTempKey(projectId: string, filename: string): string {
    return `temp/${projectId}-${filename}`;
  }

  // ── Core Operations ────────────────────────────────────────────────

  /**
   * Dosya yükle (Buffer → disk).
   */
  async upload(key: string, data: Buffer, mimeType: string): Promise<UploadResult> {
    const filePath = path.join(this.config.basePath, key);
    const dir = path.dirname(filePath);

    await fs.mkdir(dir, { recursive: true });
    await fs.writeFile(filePath, data);

    const sizeBytes = data.length;

    this.logger.debug(`📥 Yüklendi: ${key} (${this.formatBytes(sizeBytes)}, ${mimeType})`);

    return {
      key,
      url: this.getPublicUrl(key),
      bucket: 'local',
      sizeBytes,
      mimeType,
    };
  }

  /**
   * Stream olarak dosya yükle — büyük dosyalar için (Raspberry Pi bellek koruması).
   */
  async uploadFromPath(key: string, sourcePath: string, mimeType: string): Promise<UploadResult> {
    const destPath = path.join(this.config.basePath, key);
    const dir = path.dirname(destPath);

    await fs.mkdir(dir, { recursive: true });
    await fs.copyFile(sourcePath, destPath);

    const stats = await fs.stat(destPath);

    this.logger.debug(`📥 Dosyadan yüklendi: ${key} (${this.formatBytes(stats.size)})`);

    return {
      key,
      url: this.getPublicUrl(key),
      bucket: 'local',
      sizeBytes: Number(stats.size),
      mimeType,
    };
  }

  /**
   * Dosya indir (disk → Buffer).
   */
  async download(key: string): Promise<Buffer> {
    const filePath = path.join(this.config.basePath, key);
    return fs.readFile(filePath);
  }

  /**
   * Dosyanın disk yolunu döndür (FFmpeg gibi araçlar için).
   */
  getAbsolutePath(key: string): string {
    return path.join(this.config.basePath, key);
  }

  /**
   * Dosya sil.
   */
  async delete(key: string): Promise<void> {
    const filePath = path.join(this.config.basePath, key);
    try {
      await fs.unlink(filePath);
      this.logger.debug(`🗑️ Silindi: ${key}`);
    } catch {
      // Dosya bulunamadı — sessizce geç
    }
  }

  /**
   * Dosyanın mevcut olup olmadığını kontrol et.
   */
  async exists(key: string): Promise<boolean> {
    const filePath = path.join(this.config.basePath, key);
    try {
      await fs.access(filePath);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Dosya boyutunu al.
   */
  async getFileSize(key: string): Promise<number> {
    const filePath = path.join(this.config.basePath, key);
    const stats = await fs.stat(filePath);
    return Number(stats.size);
  }

  /**
   * Proje dosyalarını listele.
   */
  async listProjectFiles(projectId: string): Promise<string[]> {
    const projectDir = path.join(this.config.basePath, projectId);
    try {
      return await this.listFilesRecursive(projectDir, projectId);
    } catch {
      return [];
    }
  }

  /**
   * Proje dosyalarını tamamen temizle.
   */
  async cleanupProject(projectId: string): Promise<void> {
    const projectDir = path.join(this.config.basePath, projectId);
    try {
      await fs.rm(projectDir, { recursive: true, force: true });
      this.logger.log(`🗑️ Proje dosyaları silindi: ${projectId}`);
    } catch (error) {
      this.logger.warn(`Proje temizleme hatası: ${projectId} — ${error}`);
    }
  }

  /**
   * Temp dosyalarını temizle (24 saatten eski).
   */
  async cleanupTemp(): Promise<number> {
    const tempDir = path.join(this.config.basePath, 'temp');
    let cleaned = 0;

    try {
      const files = await fs.readdir(tempDir);
      const cutoff = Date.now() - 24 * 60 * 60 * 1000;

      for (const file of files) {
        const filePath = path.join(tempDir, file);
        const stats = await fs.stat(filePath);
        if (stats.mtimeMs < cutoff) {
          await fs.unlink(filePath);
          cleaned++;
        }
      }

      if (cleaned > 0) {
        this.logger.log(`🧹 ${cleaned} temp dosyası temizlendi`);
      }
    } catch {
      // temp dizini yoksa sorun değil
    }

    return cleaned;
  }

  /**
   * Disk kullanım istatistikleri.
   */
  async getStorageStats(): Promise<{
    totalFiles: number;
    totalSizeBytes: number;
    totalSizeHuman: string;
  }> {
    try {
      const files = await this.listFilesRecursive(this.config.basePath, '');
      let totalSize = 0;

      for (const file of files) {
        try {
          const stats = await fs.stat(path.join(this.config.basePath, file));
          totalSize += Number(stats.size);
        } catch {
          // skip
        }
      }

      return {
        totalFiles: files.length,
        totalSizeBytes: totalSize,
        totalSizeHuman: this.formatBytes(totalSize),
      };
    } catch {
      return { totalFiles: 0, totalSizeBytes: 0, totalSizeHuman: '0 B' };
    }
  }

  /**
   * Dosyanın public URL'ini oluştur.
   */
  getPublicUrl(key: string): string {
    return `${this.config.publicBaseUrl}/${key}`;
  }

  // ── Private Helpers ────────────────────────────────────────────────

  private async listFilesRecursive(dir: string, prefix: string): Promise<string[]> {
    const entries = await fs.readdir(dir, { withFileTypes: true });
    const files: string[] = [];

    for (const entry of entries) {
      const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
      if (entry.isDirectory()) {
        files.push(...(await this.listFilesRecursive(path.join(dir, entry.name), relative)));
      } else {
        files.push(relative);
      }
    }

    return files;
  }

  private formatBytes(bytes: number): string {
    if (bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
  }
}
