import { CDNService, getCDNService } from "./cdn-service";
import { queueImageProcessing, getProcessingStatus } from "./image-worker";

export interface MediaUploadOptions {
  enableCDN?: boolean;
  enableProcessing?: boolean;
  processingSizes?: string[];
  processingFormats?: string[];
  customMetadata?: Record<string, unknown>;
  tags?: string[];
  description?: string;
}

export interface MediaUploadResult {
  id: string;
  fileName: string;
  originalName: string;
  mimeType: string;
  size: number;
  url: string;
  cdnUrl?: string;
  thumbnailUrl?: string;
  uploadedAt: Date;
  processingJobId?: string;
  metadata?: Record<string, unknown>;
  tags?: string[];
  description?: string;
}

export interface MediaFile {
  id: string;
  fileName: string;
  originalName: string;
  mimeType: string;
  size: number;
  url: string;
  cdnUrl?: string;
  thumbnailUrl?: string;
  createdAt: string;
  updatedAt: string;
  downloadCount: number;
  viewCount: number;
  tags: string[];
  description?: string;
  dimensions?: {
    width: number;
    height: number;
  };
  metadata?: Record<string, unknown>;
  processingStatus?: "pending" | "processing" | "completed" | "failed";
  processingJobId?: string;
}

export class MediaService {
  private cdnService: CDNService;
  private uploadPath: string;

  constructor() {
    this.cdnService = getCDNService();
    this.uploadPath = process.env.MEDIA_UPLOAD_PATH || "./uploads";
  }

  async uploadFile(
    file: Buffer,
    originalName: string,
    mimeType: string,
    options: MediaUploadOptions = {}
  ): Promise<MediaUploadResult> {
    const fileId = this.generateFileId();
    const fileName = this.generateFileName(originalName, fileId);
    const fileKey = this.generateFileKey(fileName);

    try {
      // 로컬 저장소에 저장
      const localUrl = await this.saveToLocal(file, fileName);

      // CDN 업로드 (활성화된 경우)
      let cdnUrl: string | undefined;
      if (options.enableCDN !== false) {
        try {
          const cdnResult = await this.cdnService.upload(
            file,
            fileKey,
            mimeType
          );
          cdnUrl = cdnResult.cdnUrl || cdnResult.url;
        } catch (error) {
          console.warn("CDN upload failed, using local storage:", error);
        }
      }

      // 이미지 처리 큐에 추가 (이미지 파일인 경우)
      let processingJobId: string | undefined;
      if (options.enableProcessing !== false && mimeType.startsWith("image/")) {
        try {
          const jobId = await queueImageProcessing(fileId, localUrl, fileName, {
            sizes: (options.processingSizes as
              | ("thumbnail" | "small" | "medium" | "large" | "xlarge")[]
              | undefined) || ["thumbnail", "small", "medium", "large"],
            generateWebP: options.processingFormats?.includes("webp") || true,
            generateAVIF: options.processingFormats?.includes("avif") || false,
            quality: 85,
            preserveMetadata: true,
            optimize: true,
          });
          processingJobId = jobId;
        } catch (error) {
          console.warn("Image processing queue failed:", error);
        }
      }

      // 메타데이터 저장
      const mediaFile: MediaUploadResult = {
        id: fileId,
        fileName,
        originalName,
        mimeType,
        size: file.length,
        url: localUrl,
        cdnUrl,
        uploadedAt: new Date(),
        processingJobId,
        metadata: options.customMetadata,
        tags: options.tags,
        description: options.description,
      };

      await this.saveMetadata(mediaFile);

      return mediaFile;
    } catch (error) {
      console.error("File upload failed:", error);
      throw new Error(`파일 업로드 실패: ${error}`);
    }
  }

  async getFile(fileId: string): Promise<MediaFile | null> {
    try {
      const metadata = await this.getMetadata(fileId);
      if (!metadata) return null;

      // 처리 상태 업데이트 (처리 중인 경우)
      if (
        metadata.processingJobId &&
        metadata.processingStatus !== "completed"
      ) {
        const processingStatus = await getProcessingStatus(
          metadata.processingJobId
        );
        if (processingStatus) {
          metadata.processingStatus = processingStatus.status;
          await this.updateMetadata(fileId, {
            processingStatus: processingStatus.status,
          });
        }
      }

      return metadata;
    } catch (error) {
      console.error("Failed to get file:", error);
      return null;
    }
  }

  async getFiles(
    options: {
      page?: number;
      limit?: number;
      mimeType?: string;
      tags?: string[];
      searchTerm?: string;
      sortBy?: "name" | "date" | "size";
      sortOrder?: "asc" | "desc";
    } = {}
  ): Promise<{
    files: MediaFile[];
    total: number;
    page: number;
    limit: number;
    totalPages: number;
  }> {
    // 실제 구현에서는 데이터베이스에서 조회
    // 여기서는 Mock 데이터 반환
    const mockFiles: MediaFile[] = [];

    return {
      files: mockFiles,
      total: 0,
      page: options.page || 1,
      limit: options.limit || 20,
      totalPages: 0,
    };
  }

  async updateFile(
    fileId: string,
    updates: Partial<Pick<MediaFile, "tags" | "description" | "metadata">>
  ): Promise<MediaFile | null> {
    try {
      const existingFile = await this.getFile(fileId);
      if (!existingFile) return null;

      const updatedFile = {
        ...existingFile,
        ...updates,
        updatedAt: new Date().toISOString(),
      };

      await this.updateMetadata(fileId, updatedFile);

      // CDN에서 캐시 무효화 (메타데이터가 URL에 영향을 주는 경우)
      if (existingFile.cdnUrl) {
        try {
          await this.cdnService.invalidate([
            this.generateFileKey(existingFile.fileName),
          ]);
        } catch (error) {
          console.warn("CDN cache invalidation failed:", error);
        }
      }

      return updatedFile;
    } catch (error) {
      console.error("Failed to update file:", error);
      return null;
    }
  }

  async deleteFile(fileId: string): Promise<boolean> {
    try {
      const file = await this.getFile(fileId);
      if (!file) return false;

      // 로컬 파일 삭제
      await this.deleteFromLocal(file.fileName);

      // CDN에서 삭제
      if (file.cdnUrl) {
        try {
          await this.cdnService.delete(this.generateFileKey(file.fileName));
        } catch (error) {
          console.warn("CDN delete failed:", error);
        }
      }

      // 메타데이터 삭제
      await this.deleteMetadata(fileId);

      return true;
    } catch (error) {
      console.error("Failed to delete file:", error);
      return false;
    }
  }

  async getFileUrl(
    fileId: string,
    options?: {
      width?: number;
      height?: number;
      format?: string;
      quality?: number;
      preferCDN?: boolean;
    }
  ): Promise<string | null> {
    const file = await this.getFile(fileId);
    if (!file) return null;

    // CDN URL 사용 (가능한 경우)
    if (options?.preferCDN !== false && file.cdnUrl) {
      return this.cdnService.getUrl(
        this.generateFileKey(file.fileName),
        options
      );
    }

    return file.url;
  }

  async incrementDownloadCount(fileId: string): Promise<void> {
    const file = await this.getFile(fileId);
    if (file) {
      await this.updateMetadata(fileId, {
        downloadCount: file.downloadCount + 1,
      });
    }
  }

  async incrementViewCount(fileId: string): Promise<void> {
    const file = await this.getFile(fileId);
    if (file) {
      await this.updateMetadata(fileId, {
        viewCount: file.viewCount + 1,
      });
    }
  }

  // CDN 통계 및 상태
  async getCDNStats() {
    return this.cdnService.checkHealth();
  }

  async refreshCDNCache(fileIds: string[]): Promise<boolean> {
    try {
      const keys = await Promise.all(
        fileIds.map(async (fileId) => {
          const file = await this.getFile(fileId);
          return file ? this.generateFileKey(file.fileName) : null;
        })
      );

      const validKeys = keys.filter((key): key is string => key !== null);

      if (validKeys.length > 0) {
        await this.cdnService.invalidate(validKeys);
      }

      return true;
    } catch (error) {
      console.error("Failed to refresh CDN cache:", error);
      return false;
    }
  }

  // 헬퍼 메서드들
  private generateFileId(): string {
    return Date.now().toString() + Math.random().toString(36).substr(2, 9);
  }

  private generateFileName(originalName: string, fileId: string): string {
    const ext = originalName.split(".").pop();
    const baseName = originalName.replace(/\.[^/.]+$/, "");
    return `${fileId}_${baseName}.${ext}`;
  }

  private generateFileKey(fileName: string): string {
    return `media/${new Date().getFullYear()}/${
      new Date().getMonth() + 1
    }/${fileName}`;
  }

  private async saveToLocal(file: Buffer, fileName: string): Promise<string> {
    // 실제 구현에서는 파일 시스템에 저장
    // 여기서는 Mock URL 반환
    return `/uploads/${fileName}`;
  }

  private async saveMetadata(metadata: MediaUploadResult): Promise<void> {
    // 실제 구현에서는 데이터베이스에 저장
    console.log("Saving metadata:", metadata);
  }

  private async getMetadata(fileId: string): Promise<MediaFile | null> {
    // 실제 구현에서는 데이터베이스에서 조회
    console.log("Getting metadata for:", fileId);
    return null;
  }

  private async updateMetadata(
    fileId: string,
    updates: Partial<MediaFile>
  ): Promise<void> {
    // 실제 구현에서는 데이터베이스 업데이트
    console.log("Updating metadata:", fileId, updates);
  }

  private async deleteMetadata(fileId: string): Promise<void> {
    // 실제 구현에서는 데이터베이스에서 삭제
    console.log("Deleting metadata:", fileId);
  }

  private async deleteFromLocal(fileName: string): Promise<void> {
    // 실제 구현에서는 파일 시스템에서 삭제
    console.log("Deleting local file:", fileName);
  }
}

// 싱글톤 인스턴스
let mediaService: MediaService | null = null;

export function getMediaService(): MediaService {
  if (!mediaService) {
    mediaService = new MediaService();
  }
  return mediaService;
}
