import { Router } from 'express';
import multer from 'multer';
import { authRequired } from '../middleware/authRequired.js';
import { validateImage } from '../middleware/validateImage.js';
import { csrfRequired } from '../middleware/csrfRequired.js';
import { fileService } from '../services/file.service.js';

const router = Router();

// ─────────────────────────────────────────────────────────────────────────────
// MIME-нормализация: браузер-алиасы (Safari/Firefox/legacy) → канонические
// типы, которые принимает kie.ai. Применять до любой валидации и до загрузки
// в S3, чтобы файл лёг в MinIO с правильным Content-Type.
// ─────────────────────────────────────────────────────────────────────────────
const MIME_NORMALIZE = {
  'audio/mp3':   'audio/mpeg',
  'audio/x-m4a': 'audio/mp4',
  'audio/m4a':   'audio/mp4',
  'audio/wave':  'audio/wav',
};
const ALLOWED_VIDEO_TYPES = ['video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/webm'];
const ALLOWED_AUDIO_TYPES = ['audio/mpeg', 'audio/wav', 'audio/x-wav', 'audio/aac', 'audio/mp4', 'audio/ogg'];
const ALLOWED_MEDIA_TYPES = [...ALLOWED_VIDEO_TYPES, ...ALLOWED_AUDIO_TYPES];
const MAX_AUDIO_SIZE = 10 * 1024 * 1024;   // 10 МБ — лимит kie.ai для аудио
const MAX_VIDEO_SIZE = 500 * 1024 * 1024;  // 500 МБ — multipart upload

// Настройки multer для малых файлов (< 10MB) - для обратной совместимости
const storage = multer.memoryStorage();
const upload = multer({
  storage,
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB для простой загрузки
  },
});

// ─────────────────────────────────────────────────────────────────────────────
// Multipart Upload endpoints (прямая загрузка в S3)
// ─────────────────────────────────────────────────────────────────────────────

/**
 * POST /api/upload/video/init
 * Инициировать multipart upload для прямой загрузки в S3
 */
router.post('/video/init', authRequired, csrfRequired, async (req, res, next) => {
  try {
    const userId = req.user.id;
    const { filename, contentType, fileSize, generationUuid, generationStepId } = req.body;

    // Нормализуем браузер-алиасы (audio/mp3 → audio/mpeg и т.п.) до валидации
    const canonical = MIME_NORMALIZE[contentType] || contentType;

    // Валидация по канонической форме
    if (!ALLOWED_MEDIA_TYPES.includes(canonical)) {
      return res.status(400).json({
        ok: false,
        error: 'INVALID_CONTENT_TYPE',
        message: 'Разрешены форматы: MP4, MOV, AVI, WebM, MP3, WAV, AAC, M4A, OGG',
      });
    }

    // Размер: для аудио — 10 МБ (лимит kie.ai), для видео — 500 МБ (multipart)
    const isAudio = ALLOWED_AUDIO_TYPES.includes(canonical);
    const maxSize = isAudio ? MAX_AUDIO_SIZE : MAX_VIDEO_SIZE;
    if (!fileSize || fileSize > maxSize) {
      return res.status(400).json({
        ok: false,
        error: 'INVALID_FILE_SIZE',
        message: isAudio
          ? 'Размер аудиофайла должен быть от 1 байта до 10 МБ (требование kie.ai)'
          : 'Размер видеофайла должен быть от 1 байта до 500 МБ',
      });
    }

    if (!filename) {
      return res.status(400).json({
        ok: false,
        error: 'INVALID_FILENAME',
        message: 'Необходимо указать имя файла',
      });
    }

    const result = await fileService.initMultipartUpload({
      userId,
      filename,
      contentType: canonical,
      fileSize,
      folder: 'videos_input',
      generationUuid: generationUuid || null,
      generationStepId: generationStepId ? Number(generationStepId) : null,
    });

    res.status(201).json({
      ok: true,
      data: {
        fileId: result.fileId,
        s3Key: result.s3Key,
        uploadId: result.uploadId,
        parts: result.parts, // Array of { partNumber, presignedUrl }
        partCount: result.partCount,
      },
    });
  } catch (err) {
    next(err);
  }
});

/**
 * POST /api/upload/video/complete
 * Завершить multipart upload
 */
router.post('/video/complete', authRequired, csrfRequired, async (req, res, next) => {
  try {
    const userId = req.user.id;
    const { fileId, uploadId, parts } = req.body;

    if (!fileId || !uploadId || !parts || !Array.isArray(parts)) {
      return res.status(400).json({
        ok: false,
        error: 'BAD_REQUEST',
        message: 'Необходимо указать fileId, uploadId и parts',
      });
    }

    // Валидация parts: каждый part должен иметь ETag
    for (let i = 0; i < parts.length; i++) {
      if (!parts[i].ETag) {
        return res.status(400).json({
          ok: false,
          error: 'INVALID_PART',
          message: `Part ${i + 1} должен иметь ETag`,
        });
      }
    }

    const result = await fileService.completeMultipartUpload({
      fileId,
      userId,
      uploadId,
      parts,
    });

    res.json({
      ok: true,
      data: result,
    });
  } catch (err) {
    if (err.code === 'FILE_NOT_FOUND') {
      return res.status(404).json({
        ok: false,
        error: 'FILE_NOT_FOUND',
        message: err.message,
      });
    }
    next(err);
  }
});

/**
 * POST /api/upload/video/abort
 * Отменить multipart upload
 */
router.post('/video/abort', authRequired, csrfRequired, async (req, res, next) => {
  try {
    const userId = req.user.id;
    const { fileId, uploadId } = req.body;

    if (!fileId || !uploadId) {
      return res.status(400).json({
        ok: false,
        error: 'BAD_REQUEST',
        message: 'Необходимо указать fileId и uploadId',
      });
    }

    const result = await fileService.abortMultipartUpload({
      fileId,
      userId,
      uploadId,
    });

    res.json({
      ok: true,
      data: result,
    });
  } catch (err) {
    if (err.code === 'FILE_NOT_FOUND') {
      return res.status(404).json({
        ok: false,
        error: 'FILE_NOT_FOUND',
        message: err.message,
      });
    }
    next(err);
  }
});

// ─────────────────────────────────────────────────────────────────────────────
// Старые endpoints (для обратной совместимости с малыми файлами)
// ─────────────────────────────────────────────────────────────────────────────

/**
 * POST /api/upload/image
 * Загрузка изображения в S3
 */
router.post('/image', authRequired, csrfRequired, upload.single('file'), validateImage, async (req, res, next) => {
  try {
    const file = req.file;
    const userId = req.user.id;
    const { generationUuid, generationStepId } = req.body;

    const fileRecord = await fileService.uploadImage({
      userId,
      file,
      folder: 'images_input',
      generationUuid: generationUuid || null,
      generationStepId: generationStepId ? Number(generationStepId) : null,
    });

    res.status(201).json({
      ok: true,
      data: {
        id: fileRecord.id,
        s3Key: fileRecord.s3_key,
        filename: fileRecord.original_filename,
        size: fileRecord.file_size,
        contentType: fileRecord.content_type,
        generationUuid: fileRecord.generation_uuid,
        generationStepId: fileRecord.generation_step_id,
        createdAt: fileRecord.created_at,
      },
    });
  } catch (err) {
    next(err);
  }
});

/**
 * POST /api/upload/video
 * Загрузка видео в S3 (для малых файлов < 10MB)
 * Для больших файлов используйте /api/upload/video/init + прямая загрузка
 */
router.post('/video', authRequired, csrfRequired, upload.single('file'), async (req, res, next) => {
  try {
    const file = req.file;
    const userId = req.user.id;
    const { generationUuid, generationStepId } = req.body;

    if (!file) {
      return res.status(400).json({
        ok: false,
        error: 'NO_FILE',
        message: 'Файл не загружен',
      });
    }

    // Нормализуем браузер-алиасы (audio/mp3 → audio/mpeg и т.п.) до валидации.
    // Перезаписываем file.mimetype, чтобы и S3, и БД использовали канонический Content-Type.
    const canonical = MIME_NORMALIZE[file.mimetype] || file.mimetype;
    file.mimetype = canonical;

    // Валидация по канонической форме
    if (!ALLOWED_MEDIA_TYPES.includes(canonical)) {
      return res.status(400).json({
        ok: false,
        error: 'INVALID_FILE_TYPE',
        message: 'Разрешены форматы: MP4, MOV, AVI, WebM, MP3, WAV, AAC, M4A, OGG',
      });
    }

    const isAudio = ALLOWED_AUDIO_TYPES.includes(canonical);

    // Аудио: 10 МБ — лимит kie.ai
    if (isAudio && file.size > MAX_AUDIO_SIZE) {
      return res.status(400).json({
        ok: false,
        error: 'AUDIO_TOO_LARGE',
        message: 'Аудио до 10 МБ — требование kie.ai',
      });
    }

    // Видео > 10 МБ — отправляем на multipart endpoint
    if (!isAudio && file.size > 10 * 1024 * 1024) {
      return res.status(400).json({
        ok: false,
        error: 'FILE_TOO_LARGE_FOR_SIMPLE_UPLOAD',
        message: 'Для файлов больше 10MB используйте multipart upload: POST /api/upload/video/init',
      });
    }

    const fileRecord = await fileService.uploadVideo({
      userId,
      file,
      folder: 'videos_input',
      generationUuid: generationUuid || null,
      generationStepId: generationStepId ? Number(generationStepId) : null,
    });

    res.status(201).json({
      ok: true,
      data: {
        id: fileRecord.id,
        s3Key: fileRecord.s3_key,
        filename: fileRecord.original_filename,
        size: fileRecord.file_size,
        contentType: fileRecord.content_type,
        generationUuid: fileRecord.generation_uuid,
        generationStepId: fileRecord.generation_step_id,
        createdAt: fileRecord.created_at,
      },
    });
  } catch (err) {
    next(err);
  }
});

/**
 * POST /api/upload/url
 * Получить presigned URL для доступа к файлу
 * Требует: авторизация, CSRF, владение файлом
 */
router.post('/url', authRequired, csrfRequired, async (req, res, next) => {
  try {
    const { fileId, s3Key, expiresIn = 3600 } = req.body;

    if (!fileId && !s3Key) {
      return res.status(400).json({
        ok: false,
        error: 'BAD_REQUEST',
        message: 'Необходимо указать fileId или s3Key',
      });
    }

    const result = await fileService.getPresignedUrl({
      userId: req.user.id,
      fileId,
      s3Key,
      expiresIn: Math.min(Number(expiresIn), 86400), // макс. 24 часа
    });

    res.json({
      ok: true,
      data: result,
    });
  } catch (err) {
    if (err.code === 'FILE_NOT_FOUND') {
      return res.status(404).json({
        ok: false,
        error: 'FILE_NOT_FOUND',
        message: err.message,
      });
    }
    next(err);
  }
});

/**
 * GET /api/upload/files
 * Получить список файлов пользователя
 * Требует: авторизация
 */
router.get('/files', authRequired, async (req, res, next) => {
  try {
    const {
      fileType = 'image',
      folder = 'images_input',
      limit = 50,
      offset = 0,
    } = req.query;

    const result = await fileService.listUserFiles(req.user.id, {
      fileType,
      folder,
      limit: Math.min(Number(limit), 100),
      offset: Number(offset),
    });

    res.json({
      ok: true,
      data: result,
    });
  } catch (err) {
    next(err);
  }
});

/**
 * GET /api/upload/stats
 * Получить статистику по файлам пользователя
 * Требует: авторизация
 */
router.get('/stats', authRequired, async (req, res, next) => {
  try {
    const stats = await fileService.getFileStats(req.user.id);
    res.json({
      ok: true,
      data: stats,
    });
  } catch (err) {
    next(err);
  }
});

/**
 * DELETE /api/upload/file/:fileId
 * Удалить файл
 * Требует: авторизация, CSRF, владение файлом
 */
router.delete('/file/:fileId', authRequired, csrfRequired, async (req, res, next) => {
  try {
    const { fileId } = req.params;
    const userId = req.user.id;

    const result = await fileService.deleteFile({
      userId,
      fileId: Number(fileId),
    });

    res.json({
      ok: true,
      data: result,
    });
  } catch (err) {
    if (err.code === 'FILE_NOT_FOUND') {
      return res.status(404).json({
        ok: false,
        error: 'FILE_NOT_FOUND',
        message: err.message,
      });
    }
    next(err);
  }
});

/**
 * GET /api/upload/generation/:generationUuid/files
 * Получить файлы генерации
 * Требует: авторизация, владение генерацией
 */
router.get('/generation/:generationUuid/files', authRequired, async (req, res, next) => {
  try {
    const { generationUuid } = req.params;
    const userId = req.user.id;

    const files = await fileService.getGenerationFiles(generationUuid, userId);

    res.json({
      ok: true,
      data: files,
    });
  } catch (err) {
    next(err);
  }
});

/**
 * GET /api/upload/generation/:generationUuid/step/:stepId/files
 * Получить файлы шага генерации
 * Требует: авторизация, владение генерацией
 */
router.get('/generation/:generationUuid/step/:stepId/files', authRequired, async (req, res, next) => {
  try {
    const { generationUuid, stepId } = req.params;
    const userId = req.user.id;

    const files = await fileService.getGenerationStepFiles(generationUuid, stepId, userId);

    res.json({
      ok: true,
      data: files,
    });
  } catch (err) {
    next(err);
  }
});

/**
 * PUT /api/upload/file/:fileId/link
 * Связать файл с генерацией
 * Требует: авторизация, CSRF, владение файлом и генерацией
 */
router.put('/file/:fileId/link', authRequired, csrfRequired, async (req, res, next) => {
  try {
    const { fileId } = req.params;
    const { generationUuid, generationStepId } = req.body;
    const userId = req.user.id;

    // Проверяем владение файлом
    const fileRecord = await userFileRepository.findByIdAndOwner(Number(fileId), userId);
    if (!fileRecord) {
      return res.status(404).json({
        ok: false,
        error: 'FILE_NOT_FOUND',
        message: 'Файл не найден или у вас нет доступа к нему',
      });
    }

    // Обновляем связь
    const { pool } = await import('../db.js');
    const query = `
      UPDATE uno_bff.user_files
      SET generation_uuid = $1, generation_step_id = $2, updated_at = now()
      WHERE id = $3 AND user_id = $4
      RETURNING id, generation_uuid, generation_step_id
    `;
    const result = await pool.query(query, [generationUuid, generationStepId ? Number(generationStepId) : null, fileId, userId]);

    res.json({
      ok: true,
      data: result.rows[0],
    });
  } catch (err) {
    next(err);
  }
});

export default router;
