/**
 * Video Upload Service
 * Handles multipart video upload to backend with S3/R2
 * Epic: OQI-002 Modulo Educativo
 * Blocker: BLOCKER-003 (ST4.3)
 */

import { apiClient } from '../lib/apiClient';

// ============================================================================
// Types
// ============================================================================

export interface VideoMetadata {
  title: string;
  description: string;
  tags: string[];
  language: string;
  difficulty: 'beginner' | 'intermediate' | 'advanced' | 'expert';
  transcript?: string;
  captions?: Array<{ language: string; url: string }>;
}

export interface InitUploadRequest {
  courseId: string;
  lessonId?: string;
  filename: string;
  fileSize: number;
  contentType: string;
  metadata: VideoMetadata;
}

export interface InitUploadResponse {
  videoId: string;
  uploadId: string;
  storageKey: string;
  presignedUrls?: string[];
  uploadUrl?: string;
}

export interface UploadPart {
  partNumber: number;
  etag: string;
}

export interface Video {
  id: string;
  courseId: string;
  lessonId?: string;
  uploadedBy: string;
  title: string;
  description: string;
  originalFilename: string;
  storageProvider: string;
  storageBucket: string;
  storageKey: string;
  storageRegion?: string;
  fileSizeBytes: number;
  mimeType: string;
  durationSeconds?: number;
  status: 'uploading' | 'uploaded' | 'processing' | 'ready' | 'error' | 'deleted';
  processingStartedAt?: string;
  processingCompletedAt?: string;
  processingError?: string;
  cdnUrl?: string;
  thumbnailUrl?: string;
  transcodedVersions?: Array<{
    resolution: string;
    storageKey: string;
    cdnUrl: string;
    fileSizeBytes: number;
  }>;
  metadata: VideoMetadata;
  uploadId?: string;
  uploadPartsCompleted: number;
  uploadPartsTotal?: number;
  uploadProgressPercent: number;
  createdAt: string;
  updatedAt: string;
  uploadedAt?: string;
  deletedAt?: string;
}

export interface UploadProgressCallback {
  (progress: number, status: 'uploading' | 'processing' | 'completed' | 'error', message?: string): void;
}

// ============================================================================
// Constants
// ============================================================================

const PART_SIZE = 5 * 1024 * 1024; // 5MB per part
const MAX_CONCURRENT_UPLOADS = 3; // Upload max 3 parts in parallel

// ============================================================================
// Video Upload Service
// ============================================================================

export class VideoUploadService {
  /**
   * Initialize video upload
   */
  async initializeUpload(data: InitUploadRequest): Promise<InitUploadResponse> {
    const response = await apiClient.post<{ success: boolean; data: InitUploadResponse }>(
      '/api/v1/education/videos/upload-init',
      data
    );

    if (!response.data.success) {
      throw new Error('Failed to initialize upload');
    }

    return response.data.data;
  }

  /**
   * Upload file in parts to S3/R2 using presigned URLs
   */
  async uploadFile(
    file: File,
    presignedUrls: string[],
    onProgress?: UploadProgressCallback
  ): Promise<UploadPart[]> {
    const totalParts = presignedUrls.length;
    const parts: UploadPart[] = [];
    let uploadedParts = 0;

    // Split file into chunks
    const chunks: Blob[] = [];
    for (let i = 0; i < totalParts; i++) {
      const start = i * PART_SIZE;
      const end = Math.min(start + PART_SIZE, file.size);
      chunks.push(file.slice(start, end));
    }

    // Upload parts in batches
    for (let i = 0; i < totalParts; i += MAX_CONCURRENT_UPLOADS) {
      const batch = chunks.slice(i, Math.min(i + MAX_CONCURRENT_UPLOADS, totalParts));
      const batchIndices = Array.from({ length: batch.length }, (_, j) => i + j);

      // Upload batch in parallel
      const batchResults = await Promise.all(
        batch.map((chunk, batchIndex) => {
          const partNumber = batchIndices[batchIndex] + 1;
          const presignedUrl = presignedUrls[batchIndices[batchIndex]];
          return this.uploadPart(chunk, presignedUrl, partNumber);
        })
      );

      // Collect results
      batchResults.forEach((result, batchIndex) => {
        const partNumber = batchIndices[batchIndex] + 1;
        parts.push({
          partNumber,
          etag: result.etag,
        });
        uploadedParts++;

        // Report progress
        const progress = Math.floor((uploadedParts / totalParts) * 100);
        onProgress?.(progress, 'uploading', `Uploading part ${uploadedParts}/${totalParts}`);
      });
    }

    // Sort parts by part number
    parts.sort((a, b) => a.partNumber - b.partNumber);

    return parts;
  }

  /**
   * Upload a single part to S3/R2
   */
  private async uploadPart(
    chunk: Blob,
    presignedUrl: string,
    partNumber: number
  ): Promise<{ etag: string }> {
    const response = await fetch(presignedUrl, {
      method: 'PUT',
      body: chunk,
      headers: {
        'Content-Type': 'application/octet-stream',
      },
    });

    if (!response.ok) {
      throw new Error(`Failed to upload part ${partNumber}: ${response.statusText}`);
    }

    // S3 returns ETag in response headers
    const etag = response.headers.get('ETag');
    if (!etag) {
      throw new Error(`No ETag returned for part ${partNumber}`);
    }

    // Remove quotes from ETag if present
    return { etag: etag.replace(/"/g, '') };
  }

  /**
   * Complete multipart upload
   */
  async completeUpload(videoId: string, parts: UploadPart[]): Promise<Video> {
    const response = await apiClient.post<{ success: boolean; data: Video; message: string }>(
      `/api/v1/education/videos/${videoId}/complete`,
      { parts }
    );

    if (!response.data.success) {
      throw new Error('Failed to complete upload');
    }

    return response.data.data;
  }

  /**
   * Abort multipart upload
   */
  async abortUpload(videoId: string): Promise<void> {
    await apiClient.post(`/api/v1/education/videos/${videoId}/abort`);
  }

  /**
   * Get video by ID
   */
  async getVideo(videoId: string): Promise<Video> {
    const response = await apiClient.get<{ success: boolean; data: Video }>(
      `/api/v1/education/videos/${videoId}`
    );

    if (!response.data.success) {
      throw new Error('Failed to get video');
    }

    return response.data.data;
  }

  /**
   * Delete video
   */
  async deleteVideo(videoId: string): Promise<void> {
    await apiClient.delete(`/api/v1/education/videos/${videoId}`);
  }

  /**
   * Full upload flow: initialize -> upload parts -> complete
   */
  async uploadVideo(
    file: File,
    request: Omit<InitUploadRequest, 'filename' | 'fileSize' | 'contentType'>,
    onProgress?: UploadProgressCallback
  ): Promise<Video> {
    try {
      // Step 1: Initialize upload
      onProgress?.(0, 'uploading', 'Initializing upload...');

      const initData: InitUploadRequest = {
        ...request,
        filename: file.name,
        fileSize: file.size,
        contentType: file.type,
      };

      const { videoId, presignedUrls } = await this.initializeUpload(initData);

      if (!presignedUrls || presignedUrls.length === 0) {
        throw new Error('No presigned URLs received');
      }

      // Step 2: Upload file parts
      onProgress?.(0, 'uploading', 'Uploading video...');
      const parts = await this.uploadFile(file, presignedUrls, onProgress);

      // Step 3: Complete upload
      onProgress?.(100, 'processing', 'Finalizing upload...');
      const video = await this.completeUpload(videoId, parts);

      // Step 4: Complete
      onProgress?.(100, 'completed', 'Upload complete!');

      return video;
    } catch (error) {
      onProgress?.(0, 'error', error instanceof Error ? error.message : 'Upload failed');
      throw error;
    }
  }
}

// ============================================================================
// Default Instance
// ============================================================================

export const videoUploadService = new VideoUploadService();
