import { userFileRepository } from '../repositories/user-file.repository.js';
import {
  uploadFile as s3UploadFile,
  getPresignedUrl as s3GetPresignedUrl,
  deleteFile as s3DeleteFile,
  getImageInputKey,
  getVideoInputKey,
  createMultipartUpload as s3CreateMultipartUpload,
  getPresignedPartUrl as s3GetPresignedPartUrl,
  completeMultipartUpload as s3CompleteMultipartUpload,
  abortMultipartUpload as s3AbortMultipartUpload,
} from './s3.service.js';

/**
 * Сервис для управления файлами пользователей
 */
export const fileService = {
  /**
   * Загрузить изображение в S3
   */
  async uploadImage({ userId, file, folder = 'images_input', generationUuid = null, generationStepId = null }) {
    console.log('[fileService.uploadImage] userId:', userId, 'file:', file?.originalname, 'folder:', folder);

    const s3Key = getImageInputKey(file.originalname, userId, folder);
    await s3UploadFile(s3Key, file.buffer, file.mimetype);

    const fileRecord = await userFileRepository.create({
      userId,
      s3Key,
      originalFilename: file.originalname,
      fileSize: file.size,
      contentType: file.mimetype,
      fileType: 'image',
      folder,
      generationUuid,
      generationStepId,
    });

    return fileRecord;
  },

  /**
   * Загрузить видео в S3
   */
  async uploadVideo({ userId, file, folder = 'videos_input', generationUuid = null, generationStepId = null }) {
    console.log('[fileService.uploadVideo] userId:', userId, 'file:', file?.originalname, 'folder:', folder);

    const s3Key = getVideoInputKey(file.originalname, userId, folder);
    await s3UploadFile(s3Key, file.buffer, file.mimetype);

    const fileRecord = await userFileRepository.create({
      userId,
      s3Key,
      originalFilename: file.originalname,
      fileSize: file.size,
      contentType: file.mimetype,
      fileType: 'video',
      folder,
      generationUuid,
      generationStepId,
    });

    return fileRecord;
  },

  /**
   * Инициировать multipart upload для прямой загрузки в S3
   */
  async initMultipartUpload({ userId, filename, contentType, fileSize, folder = 'videos_input', generationUuid = null, generationStepId = null }) {
    console.log('[fileService.initMultipartUpload] userId:', userId, 'filename:', filename, 'size:', fileSize);

    const s3Key = getVideoInputKey(filename, userId, folder);

    // Инициируем multipart upload в S3
    const { uploadId } = await s3CreateMultipartUpload(s3Key, contentType);

    // Создаём запись в БД со статусом "uploading" и uploadId
    const fileRecord = await userFileRepository.create({
      userId,
      s3Key,
      originalFilename: filename,
      fileSize,
      contentType,
      fileType: 'video',
      folder,
      generationUuid,
      generationStepId,
      status: 'uploading',
      uploadId, // Сохраняем uploadId в БД
    });

    // Вычисляем количество частей (10MB на часть - оптимизировано для скорости)
    const PART_SIZE = 10 * 1024 * 1024; // 10MB
    const partCount = Math.ceil(fileSize / PART_SIZE);

    // Создаём presigned URLs для всех частей
    const parts = [];
    for (let i = 1; i <= partCount; i++) {
      const presignedUrl = await s3GetPresignedPartUrl(s3Key, uploadId, i, 3600); // 1 час на загрузку части
      parts.push({
        partNumber: i,
        presignedUrl,
      });
    }

    return {
      fileId: fileRecord.id,
      s3Key,
      uploadId,
      parts,
      partCount,
    };
  },

  /**
   * Завершить multipart upload
   */
  async completeMultipartUpload({ fileId, userId, uploadId, parts }) {
    console.log('[fileService.completeMultipartUpload] fileId:', fileId, 'uploadId:', uploadId);

    // Проверяем владение файлом
    const fileRecord = await userFileRepository.findByIdAndOwner(fileId, userId);
    if (!fileRecord) {
      const error = new Error('Файл не найден');
      error.code = 'FILE_NOT_FOUND';
      throw error;
    }

    // Завершаем multipart upload в S3
    const result = await s3CompleteMultipartUpload(fileRecord.s3_key, uploadId, parts);

    // Обновляем статус файла в БД
    await userFileRepository.update(fileId, {
      status: 'uploaded',
      updated_at: new Date(),
    });

    return {
      fileId: fileRecord.id,
      s3Key: fileRecord.s3_key,
      location: result.location,
      etag: result.etag,
    };
  },

  /**
   * Отменить multipart upload
   */
  async abortMultipartUpload({ fileId, userId, uploadId }) {
    console.log('[fileService.abortMultipartUpload] fileId:', fileId);

    const fileRecord = await userFileRepository.findByIdAndOwner(fileId, userId);
    if (!fileRecord) {
      const error = new Error('Файл не найден');
      error.code = 'FILE_NOT_FOUND';
      throw error;
    }

    // Отменяем multipart upload в S3
    await s3AbortMultipartUpload(fileRecord.s3_key, uploadId);

    // Удаляем запись из БД
    await userFileRepository.delete(fileId);

    return { fileId, aborted: true };
  },

  /**
   * Получить URL для файла с проверкой владения
   * Если файл в публичной папке — возвращается прямой URL, иначе presigned
   */
  async getPresignedUrl({ userId, fileId, s3Key, expiresIn }) {
    let fileRecord;

    // Поиск файла по ID или по ключу
    if (fileId) {
      fileRecord = await userFileRepository.findByIdAndOwner(fileId, userId);
    } else if (s3Key) {
      fileRecord = await userFileRepository.findByKeyAndOwner(s3Key, userId);
    }

    if (!fileRecord) {
      const error = new Error('Файл не найден или у вас нет доступа к нему');
      error.code = 'FILE_NOT_FOUND';
      error.statusCode = 404;
      throw error;
    }

    // Для публичных папок возвращаем прямой URL через nginx
    const publicFolders = ['images_input', 'videos_input', 'videos_output'];
    const isPublic = publicFolders.includes(fileRecord.folder);
    
    let url;
    if (isPublic) {
      // Прямой URL через nginx прокси
      url = `/files/${fileRecord.s3_key}`;
    } else {
      // Presigned URL для приватных файлов
      url = await s3GetPresignedUrl(fileRecord.s3_key, expiresIn);
    }

    return {
      id: fileRecord.id,
      s3Key: fileRecord.s3_key,
      originalFilename: fileRecord.original_filename,
      contentType: fileRecord.content_type,
      fileSize: fileRecord.file_size,
      url,
      isPublic,
      expiresIn: isPublic ? null : (expiresIn || 3600),
    };
  },

  /**
   * Получить список файлов пользователя
   */
  async listUserFiles(userId, options = {}) {
    const defaultOptions = {
      fileType: 'image',
      folder: 'images_input',
      limit: 50,
      offset: 0,
    };

    const mergedOptions = { ...defaultOptions, ...options };
    const [files, total] = await Promise.all([
      userFileRepository.findByUser(userId, mergedOptions),
      userFileRepository.countByUser(userId, mergedOptions),
    ]);

    return {
      files: files.map((f) => ({
        id: f.id,
        s3Key: f.s3_key,
        originalFilename: f.original_filename,
        contentType: f.content_type,
        fileSize: f.file_size,
        fileType: f.file_type,
        folder: f.folder,
        createdAt: f.created_at,
      })),
      total,
      limit: mergedOptions.limit,
      offset: mergedOptions.offset,
    };
  },

  /**
   * Удалить файл пользователя (из S3 и БД)
   */
  async deleteFile({ userId, fileId, s3Key }) {
    let fileRecord;

    // Поиск файла с проверкой владения
    if (fileId) {
      fileRecord = await userFileRepository.deleteByIdAndOwner(fileId, userId);
    } else if (s3Key) {
      fileRecord = await userFileRepository.deleteByKeyAndOwner(s3Key, userId);
    }

    if (!fileRecord) {
      const error = new Error('Файл не найден или у вас нет доступа к нему');
      error.code = 'FILE_NOT_FOUND';
      error.statusCode = 404;
      throw error;
    }

    // Удаление из S3
    try {
      await s3DeleteFile(fileRecord.s3_key);
    } catch (err) {
      // Логируем ошибку, но не прерываем процесс
      // Файл уже удалён из БД
      console.error(`Ошибка удаления файла из S3: ${fileRecord.s3_key}`, err);
    }

    return {
      id: fileRecord.id,
      s3Key: fileRecord.s3_key,
      deleted: true,
    };
  },

  /**
   * Получить статистику по файлам пользователя
   */
  async getFileStats(userId) {
    const { pool } = await import('../db.js');
    
    const [imageCount, videoCount, totalSize] = await Promise.all([
      userFileRepository.countByUser(userId, { fileType: 'image' }),
      userFileRepository.countByUser(userId, { fileType: 'video' }),
      (async () => {
        const query = `
          SELECT COALESCE(SUM(file_size), 0) as total
          FROM uno_bff.user_files
          WHERE user_id = $1
        `;
        const result = await pool.query(query, [userId]);
        return parseInt(result.rows[0].total, 10);
      })(),
    ]);

    return {
      imageCount,
      videoCount,
      totalCount: imageCount + videoCount,
      totalSizeBytes: totalSize,
      totalSizeMB: Math.round((totalSize / (1024 * 1024)) * 100) / 100,
    };
  },

  /**
   * Получить файлы генерации
   */
  async getGenerationFiles(generationUuid, userId) {
    const files = await userFileRepository.findByGeneration(generationUuid, userId);
    
    return files.map((f) => ({
      id: f.id,
      s3Key: f.s3_key,
      originalFilename: f.original_filename,
      contentType: f.content_type,
      fileSize: f.file_size,
      fileType: f.file_type,
      folder: f.folder,
      generationUuid: f.generation_uuid,
      generationStepId: f.generation_step_id,
      createdAt: f.created_at,
    }));
  },

  /**
   * Получить файлы шага генерации
   */
  async getGenerationStepFiles(generationUuid, stepId, userId) {
    const files = await userFileRepository.findByGenerationStep(generationUuid, stepId, userId);
    
    return files.map((f) => ({
      id: f.id,
      s3Key: f.s3_key,
      originalFilename: f.original_filename,
      contentType: f.content_type,
      fileSize: f.file_size,
      fileType: f.file_type,
      folder: f.folder,
      generationUuid: f.generation_uuid,
      generationStepId: f.generation_step_id,
      createdAt: f.created_at,
    }));
  },
};
