import { Router, Request, Response } from 'express';
import { prisma } from '../lib/prisma.js';
import { WordType, CefrLevel, MediaKind } from '@prisma/client';
import { isAuthenticated, isAdmin } from '../middleware/auth.js';
import { videoUpload, handleUploadError } from '../middleware/upload.js';
import { generateGifFromVideo } from '../utils/gifGenerator.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const router = Router();

/**
 * GET /api/terms
 * Get all terms with optional filtering, search, and pagination
 * Query params:
 * - query: search text (searches wordText and normalizedText)
 * - wordType: filter by word type (NOUN, VERB, etc.)
 * - cefrLevel: filter by CEFR level (A1, A2, B1, B2, C1, C2)
 * - page: page number (default: 1)
 * - limit: items per page (default: 20)
 */
router.get('/', async (req: Request, res: Response) => {
  try {
    const {
      query = '',
      wordType,
      cefrLevel,
      page = '1',
      limit = '20',
    } = req.query;

    const pageNum = parseInt(page as string, 10);
    const limitNum = parseInt(limit as string, 10);
    const skip = (pageNum - 1) * limitNum;

    // Build where clause
    const where: any = {};

    // Add search filter
    if (query && typeof query === 'string' && query.trim()) {
      where.OR = [
        { wordText: { contains: query.trim() } },
        { normalizedText: { contains: query.trim() } },
      ];
    }

    // Add wordType filter
    if (wordType && typeof wordType === 'string') {
      const validWordTypes = Object.values(WordType);
      if (validWordTypes.includes(wordType as WordType)) {
        where.wordType = wordType as WordType;
      }
    }

    // Add cefrLevel filter
    if (cefrLevel && typeof cefrLevel === 'string') {
      const validCefrLevels = Object.values(CefrLevel);
      if (validCefrLevels.includes(cefrLevel as CefrLevel)) {
        where.cefrLevel = cefrLevel as CefrLevel;
      }
    }

    // Get total count for pagination
    const total = await prisma.term.count({ where });

    // Get terms with media
    const terms = await prisma.term.findMany({
      where,
      include: {
        media: {
          orderBy: {
            createdAt: 'asc',
          },
        },
        examples: {
          orderBy: {
            createdAt: 'asc',
          },
        },
      },
      orderBy: {
        wordText: 'asc',
      },
      skip,
      take: limitNum,
    });

    res.json({
      terms,
      pagination: {
        page: pageNum,
        limit: limitNum,
        total,
        totalPages: Math.ceil(total / limitNum),
      },
    });
  } catch (error: any) {
    console.error('Error fetching terms:', error);
    res.status(500).json({ error: 'Failed to fetch terms', message: error.message });
  }
});

/**
 * GET /api/terms/:id
 * Get a single term by ID with all related data
 */
router.get('/:id', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;

    const term = await prisma.term.findUnique({
      where: { id },
      include: {
        media: {
          orderBy: {
            createdAt: 'asc',
          },
        },
        examples: {
          orderBy: {
            createdAt: 'asc',
          },
        },
      },
    });

    if (!term) {
      return res.status(404).json({ error: 'Term not found' });
    }

    res.json({ term });
  } catch (error: any) {
    console.error('Error fetching term:', error);
    res.status(500).json({ error: 'Failed to fetch term', message: error.message });
  }
});

/**
 * Helper function to normalize text (lowercase, remove diacritics)
 */
function normalizeText(text: string): string {
  return text
    .toLowerCase()
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, ''); // Remove diacritics
}

/**
 * POST /api/terms
 * Create a new term (Admin only)
 */
router.post('/', isAuthenticated, isAdmin, async (req: Request, res: Response) => {
  try {
    const { wordText, wordType, cefrLevel, shortDescription, tags, iconAssetId } = req.body;

    // Validate required fields
    if (!wordText || !wordType || !cefrLevel) {
      return res.status(400).json({
        error: 'Validation error',
        message: 'wordText, wordType, and cefrLevel are required',
      });
    }

    // Validate wordType
    const validWordTypes = Object.values(WordType);
    if (!validWordTypes.includes(wordType as WordType)) {
      return res.status(400).json({
        error: 'Validation error',
        message: `Invalid wordType. Must be one of: ${validWordTypes.join(', ')}`,
      });
    }

    // Validate cefrLevel
    const validCefrLevels = Object.values(CefrLevel);
    if (!validCefrLevels.includes(cefrLevel as CefrLevel)) {
      return res.status(400).json({
        error: 'Validation error',
        message: `Invalid cefrLevel. Must be one of: ${validCefrLevels.join(', ')}`,
      });
    }

    // Create term with auto-generated normalizedText
    const term = await prisma.term.create({
      data: {
        wordText,
        normalizedText: normalizeText(wordText),
        wordType: wordType as WordType,
        cefrLevel: cefrLevel as CefrLevel,
        shortDescription: shortDescription || null,
        tags: tags || null,
        iconAssetId: iconAssetId || null,
      },
      include: {
        media: true,
        examples: true,
      },
    });

    res.status(201).json({ term });
  } catch (error: any) {
    console.error('Error creating term:', error);
    res.status(500).json({ error: 'Failed to create term', message: error.message });
  }
});

/**
 * PUT /api/terms/:id
 * Update an existing term (Admin only)
 */
router.put('/:id', isAuthenticated, isAdmin, async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { wordText, wordType, cefrLevel, shortDescription, tags, iconAssetId } = req.body;

    // Check if term exists
    const existingTerm = await prisma.term.findUnique({ where: { id } });
    if (!existingTerm) {
      return res.status(404).json({ error: 'Term not found' });
    }

    // Build update data
    const updateData: any = {};

    if (wordText !== undefined) {
      updateData.wordText = wordText;
      updateData.normalizedText = normalizeText(wordText);
    }

    if (wordType !== undefined) {
      const validWordTypes = Object.values(WordType);
      if (!validWordTypes.includes(wordType as WordType)) {
        return res.status(400).json({
          error: 'Validation error',
          message: `Invalid wordType. Must be one of: ${validWordTypes.join(', ')}`,
        });
      }
      updateData.wordType = wordType as WordType;
    }

    if (cefrLevel !== undefined) {
      const validCefrLevels = Object.values(CefrLevel);
      if (!validCefrLevels.includes(cefrLevel as CefrLevel)) {
        return res.status(400).json({
          error: 'Validation error',
          message: `Invalid cefrLevel. Must be one of: ${validCefrLevels.join(', ')}`,
        });
      }
      updateData.cefrLevel = cefrLevel as CefrLevel;
    }

    if (shortDescription !== undefined) updateData.shortDescription = shortDescription || null;
    if (tags !== undefined) updateData.tags = tags || null;
    if (iconAssetId !== undefined) updateData.iconAssetId = iconAssetId || null;

    // Update term
    const term = await prisma.term.update({
      where: { id },
      data: updateData,
      include: {
        media: true,
        examples: true,
      },
    });

    res.json({ term });
  } catch (error: any) {
    console.error('Error updating term:', error);
    res.status(500).json({ error: 'Failed to update term', message: error.message });
  }
});

/**
 * DELETE /api/terms/:id
 * Delete a term and its associated media (Admin only)
 */
router.delete('/:id', isAuthenticated, isAdmin, async (req: Request, res: Response) => {
  try {
    const { id } = req.params;

    // Check if term exists and get associated media
    const term = await prisma.term.findUnique({
      where: { id },
      include: { media: true },
    });

    if (!term) {
      return res.status(404).json({ error: 'Term not found' });
    }

    // Delete associated video files from disk
    const uploadsDir = path.join(__dirname, '..', '..', 'uploads', 'videos');
    for (const media of term.media) {
      if (media.kind === MediaKind.VIDEO && media.url) {
        const filename = path.basename(media.url);
        const filePath = path.join(uploadsDir, filename);

        try {
          if (fs.existsSync(filePath)) {
            fs.unlinkSync(filePath);
            console.log(`Deleted video file: ${filename}`);
          }
        } catch (err) {
          console.error(`Failed to delete video file ${filename}:`, err);
        }
      }
    }

    // Delete term (cascade will delete TermMedia records)
    await prisma.term.delete({ where: { id } });

    res.json({ message: 'Term deleted successfully' });
  } catch (error: any) {
    console.error('Error deleting term:', error);
    res.status(500).json({ error: 'Failed to delete term', message: error.message });
  }
});

/**
 * POST /api/terms/:id/media
 * Upload a video for a term (Admin only)
 */
router.post(
  '/:id/media',
  isAuthenticated,
  isAdmin,
  videoUpload.single('video'),
  handleUploadError,
  async (req: Request, res: Response) => {
    try {
      const { id } = req.params;

      // Check if term exists
      const term = await prisma.term.findUnique({ where: { id } });
      if (!term) {
        // Clean up uploaded file if term doesn't exist
        if (req.file) {
          fs.unlinkSync(req.file.path);
        }
        return res.status(404).json({ error: 'Term not found' });
      }

      // Check if file was uploaded
      if (!req.file) {
        return res.status(400).json({ error: 'No video file uploaded' });
      }

      // Create relative URL path
      const relativeUrl = `/uploads/videos/${req.file.filename}`;

      // Create TermMedia record for video
      const media = await prisma.termMedia.create({
        data: {
          termId: id,
          kind: MediaKind.VIDEO,
          url: relativeUrl,
          // TODO: Extract video metadata (duration, dimensions) using ffprobe if available
          // For now, these fields will be null
        },
      });

      // Generate GIF preview from video
      try {
        const videoPath = req.file.path;
        const gifFilename = req.file.filename.replace(/\.(mp4|webm|mov)$/i, '.gif');
        const gifsDir = path.join(__dirname, '..', '..', 'uploads', 'gifs');

        // Ensure gifs directory exists
        if (!fs.existsSync(gifsDir)) {
          fs.mkdirSync(gifsDir, { recursive: true });
        }

        const gifPath = path.join(gifsDir, gifFilename);
        const gifRelativeUrl = `/uploads/gifs/${gifFilename}`;

        // Generate GIF (300px width, 10fps, first 3 seconds)
        await generateGifFromVideo(videoPath, gifPath, {
          width: 300,
          fps: 10,
          duration: 3,
          startTime: 0,
        });

        // Create TermMedia record for GIF
        await prisma.termMedia.create({
          data: {
            termId: id,
            kind: MediaKind.GIF,
            url: gifRelativeUrl,
          },
        });

        console.log(`Generated GIF preview: ${gifRelativeUrl}`);
      } catch (gifError: any) {
        // Log error but don't fail the upload if GIF generation fails
        console.error('Failed to generate GIF preview:', gifError.message);
      }

      res.status(201).json({ media });
    } catch (error: any) {
      console.error('Error uploading video:', error);

      // Clean up uploaded file on error
      if (req.file) {
        try {
          fs.unlinkSync(req.file.path);
        } catch (err) {
          console.error('Failed to clean up file:', err);
        }
      }

      res.status(500).json({ error: 'Failed to upload video', message: error.message });
    }
  }
);

/**
 * DELETE /api/terms/:id/media/:mediaId
 * Delete a video from a term (Admin only)
 */
router.delete('/:id/media/:mediaId', isAuthenticated, isAdmin, async (req: Request, res: Response) => {
  try {
    const { id, mediaId } = req.params;

    // Check if media exists and belongs to the term
    const media = await prisma.termMedia.findUnique({
      where: { id: mediaId },
    });

    if (!media) {
      return res.status(404).json({ error: 'Media not found' });
    }

    if (media.termId !== id) {
      return res.status(400).json({ error: 'Media does not belong to this term' });
    }

    // Delete video file from disk if it's a video
    if (media.kind === MediaKind.VIDEO && media.url) {
      const uploadsDir = path.join(__dirname, '..', '..', 'uploads', 'videos');
      const filename = path.basename(media.url);
      const filePath = path.join(uploadsDir, filename);

      try {
        if (fs.existsSync(filePath)) {
          fs.unlinkSync(filePath);
          console.log(`Deleted video file: ${filename}`);
        }
      } catch (err) {
        console.error(`Failed to delete video file ${filename}:`, err);
      }

      // Also delete corresponding GIF if it exists
      const gifFilename = filename.replace(/\.(mp4|webm|mov)$/i, '.gif');
      const gifsDir = path.join(__dirname, '..', '..', 'uploads', 'gifs');
      const gifPath = path.join(gifsDir, gifFilename);
      const gifRelativeUrl = `/uploads/gifs/${gifFilename}`;

      try {
        if (fs.existsSync(gifPath)) {
          fs.unlinkSync(gifPath);
          console.log(`Deleted GIF file: ${gifFilename}`);
        }

        // Delete GIF media record
        const gifMedia = await prisma.termMedia.findFirst({
          where: {
            termId: id,
            kind: MediaKind.GIF,
            url: gifRelativeUrl,
          },
        });

        if (gifMedia) {
          await prisma.termMedia.delete({ where: { id: gifMedia.id } });
          console.log(`Deleted GIF media record: ${gifMedia.id}`);
        }
      } catch (err) {
        console.error(`Failed to delete GIF ${gifFilename}:`, err);
      }
    }

    // Delete GIF file from disk if it's a GIF
    if (media.kind === MediaKind.GIF && media.url) {
      const gifsDir = path.join(__dirname, '..', '..', 'uploads', 'gifs');
      const filename = path.basename(media.url);
      const filePath = path.join(gifsDir, filename);

      try {
        if (fs.existsSync(filePath)) {
          fs.unlinkSync(filePath);
          console.log(`Deleted GIF file: ${filename}`);
        }
      } catch (err) {
        console.error(`Failed to delete GIF file ${filename}:`, err);
      }
    }

    // Delete TermMedia record
    await prisma.termMedia.delete({ where: { id: mediaId } });

    res.json({ message: 'Media deleted successfully' });
  } catch (error: any) {
    console.error('Error deleting media:', error);
    res.status(500).json({ error: 'Failed to delete media', message: error.message });
  }
});

/**
 * POST /api/terms/:id/media/:mediaId/regenerate-gif
 * Regenerate GIF preview for a video (Admin only)
 */
router.post('/:id/media/:mediaId/regenerate-gif', isAuthenticated, isAdmin, async (req: Request, res: Response) => {
  try {
    const { id, mediaId } = req.params;

    // Check if media exists and belongs to the term
    const media = await prisma.termMedia.findUnique({
      where: { id: mediaId },
    });

    if (!media) {
      return res.status(404).json({ error: 'Media not found' });
    }

    if (media.termId !== id) {
      return res.status(400).json({ error: 'Media does not belong to this term' });
    }

    if (media.kind !== MediaKind.VIDEO) {
      return res.status(400).json({ error: 'Can only regenerate GIF from video media' });
    }

    // Get video file path
    const videoFilename = path.basename(media.url);
    const videoPath = path.join(__dirname, '..', '..', 'uploads', 'videos', videoFilename);

    if (!fs.existsSync(videoPath)) {
      return res.status(404).json({ error: 'Video file not found on disk' });
    }

    // Generate GIF filename and path
    const gifFilename = videoFilename.replace(/\.(mp4|webm|mov)$/i, '.gif');
    const gifsDir = path.join(__dirname, '..', '..', 'uploads', 'gifs');

    // Ensure gifs directory exists
    if (!fs.existsSync(gifsDir)) {
      fs.mkdirSync(gifsDir, { recursive: true });
    }

    const gifPath = path.join(gifsDir, gifFilename);
    const gifRelativeUrl = `/uploads/gifs/${gifFilename}`;

    // Delete old GIF if it exists
    if (fs.existsSync(gifPath)) {
      fs.unlinkSync(gifPath);
    }

    // Delete old GIF media record if it exists
    const existingGif = await prisma.termMedia.findFirst({
      where: {
        termId: id,
        kind: MediaKind.GIF,
        url: gifRelativeUrl,
      },
    });

    if (existingGif) {
      await prisma.termMedia.delete({ where: { id: existingGif.id } });
    }

    // Generate new GIF (300px width, 10fps, first 3 seconds)
    await generateGifFromVideo(videoPath, gifPath, {
      width: 300,
      fps: 10,
      duration: 3,
      startTime: 0,
    });

    // Create new TermMedia record for GIF
    const gifMedia = await prisma.termMedia.create({
      data: {
        termId: id,
        kind: MediaKind.GIF,
        url: gifRelativeUrl,
      },
    });

    console.log(`Regenerated GIF preview: ${gifRelativeUrl}`);
    res.status(201).json({ media: gifMedia });
  } catch (error: any) {
    console.error('Error regenerating GIF:', error);
    res.status(500).json({ error: 'Failed to regenerate GIF', message: error.message });
  }
});

/**
 * POST /api/terms/regenerate-all-gifs
 * Regenerate GIF previews for ALL videos (Admin only)
 * This is useful after deployment to production
 */
router.post('/regenerate-all-gifs', isAuthenticated, isAdmin, async (_req: Request, res: Response) => {
  try {
    console.log('Starting bulk GIF regeneration...');

    // Get all video media
    const videoMedia = await prisma.termMedia.findMany({
      where: { kind: MediaKind.VIDEO },
      include: { term: true },
    });

    console.log(`Found ${videoMedia.length} videos to process`);

    const results = {
      total: videoMedia.length,
      success: 0,
      failed: 0,
      errors: [] as string[],
    };

    for (const media of videoMedia) {
      try {
        const videoFilename = path.basename(media.url);
        const videoPath = path.join(__dirname, '..', '..', 'uploads', 'videos', videoFilename);

        // Check if video file exists
        if (!fs.existsSync(videoPath)) {
          console.log(`Video file not found: ${videoFilename}`);
          results.failed++;
          results.errors.push(`${media.term.wordText}: Video file not found`);
          continue;
        }

        // Generate GIF filename
        const gifFilename = videoFilename.replace(/\.(mp4|webm|mov)$/i, '.gif');
        const gifsDir = path.join(__dirname, '..', '..', 'uploads', 'gifs');
        const gifPath = path.join(gifsDir, gifFilename);
        const gifRelativeUrl = `/uploads/gifs/${gifFilename}`;

        // Ensure gifs directory exists
        if (!fs.existsSync(gifsDir)) {
          fs.mkdirSync(gifsDir, { recursive: true });
        }

        // Delete old GIF if exists
        const existingGif = await prisma.termMedia.findFirst({
          where: {
            termId: media.termId,
            kind: MediaKind.GIF,
            url: gifRelativeUrl,
          },
        });

        if (existingGif) {
          await prisma.termMedia.delete({ where: { id: existingGif.id } });
          if (fs.existsSync(gifPath)) {
            fs.unlinkSync(gifPath);
          }
        }

        // Generate GIF
        console.log(`Generating GIF for: ${media.term.wordText}`);
        await generateGifFromVideo(videoPath, gifPath, {
          fps: 10,
          width: 300,
          duration: 3,
          startTime: 0,
        });

        // Create GIF media record
        await prisma.termMedia.create({
          data: {
            termId: media.termId,
            kind: MediaKind.GIF,
            url: gifRelativeUrl,
          },
        });

        console.log(`Success: ${media.term.wordText}`);
        results.success++;
      } catch (error: any) {
        console.error(`Failed: ${media.term.wordText} - ${error.message}`);
        results.failed++;
        results.errors.push(`${media.term.wordText}: ${error.message}`);
      }
    }

    console.log('Bulk GIF regeneration complete:', results);
    res.json(results);
  } catch (error: any) {
    console.error('Error in bulk GIF regeneration:', error);
    res.status(500).json({ error: 'Failed to regenerate GIFs', message: error.message });
  }
});

export default router;

