import { Router, Request, Response } from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import { ulid } from 'ulid';
import slugify from 'slugify';
import { prisma } from '../db/prisma';
import { requireAuth, requires } from '../middleware/auth';

const REPO_ROOT = path.resolve(__dirname, '../../..');
const STORAGE_PATH = process.env.MEDIA_STORAGE_PATH
  ? path.isAbsolute(process.env.MEDIA_STORAGE_PATH)
    ? process.env.MEDIA_STORAGE_PATH
    : path.resolve(REPO_ROOT, process.env.MEDIA_STORAGE_PATH)
  : path.resolve(REPO_ROOT, 'storage/media');

function ensureStorageDir() {
  fs.mkdirSync(STORAGE_PATH, { recursive: true });
}

const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 100 * 1024 * 1024 }, // 100MB
});

const IMAGE_MIMES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'];
const VIDEO_MIMES = ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime'];
const ALLOWED_MIMES = [...IMAGE_MIMES, ...VIDEO_MIMES];

function getMediaType(mimeType: string): 'image' | 'video' | null {
  if (IMAGE_MIMES.includes(mimeType)) return 'image';
  if (VIDEO_MIMES.includes(mimeType)) return 'video';
  return null;
}

function makeSlug(filename: string): string {
  const name = path.parse(filename).name;
  return slugify(name, { lower: true, strict: true }) || 'media';
}

const router = Router();

router.post(
  '/upload',
  requireAuth,
  requires('gallery.upload'),
  upload.single('file'),
  async (req: Request, res: Response) => {
    try {
      const file = req.file;
      if (!file) {
        res.status(400).json({ error: 'No file provided' });
        return;
      }

      if (!ALLOWED_MIMES.includes(file.mimetype)) {
        res.status(400).json({ error: `Unsupported file type: ${file.mimetype}` });
        return;
      }

      const mediaType = getMediaType(file.mimetype);
      if (!mediaType) {
        res.status(400).json({ error: 'Could not determine media type' });
        return;
      }

      const id = ulid();
      const slug = makeSlug(file.originalname);

      ensureStorageDir();

      const filePath = path.join(STORAGE_PATH, id);
      fs.writeFileSync(filePath, file.buffer);

      const metaPath = path.join(STORAGE_PATH, `${id}.json`);
      fs.writeFileSync(metaPath, JSON.stringify({
        mimeType: file.mimetype,
        type: mediaType,
        size: file.size,
      }));

      const media = await prisma.media.create({
        data: {
          id,
          slug,
          type: mediaType,
          mimeType: file.mimetype,
          size: file.size,
          originalFilename: file.originalname,
          uploadedBy: req.user!.pubkey,
        },
      });

      res.status(201).json({
        id: media.id,
        slug: media.slug,
        url: `/media/${media.id}`,
      });
    } catch (err) {
      console.error('Upload media error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

router.get('/', async (_req: Request, res: Response) => {
  try {
    const media = await prisma.media.findMany({
      orderBy: { createdAt: 'desc' },
    });

    const result = media.map((m) => ({
      ...m,
      url: `/media/${m.id}`,
    }));

    res.json(result);
  } catch (err) {
    console.error('List media error:', err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

router.get('/:id', async (req: Request, res: Response) => {
  try {
    const media = await prisma.media.findUnique({
      where: { id: req.params.id as string },
    });

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

    res.json({ ...media, url: `/media/${media.id}` });
  } catch (err) {
    console.error('Get media error:', err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

router.patch(
  '/:id',
  requireAuth,
  requires('gallery.upload'),
  async (req: Request, res: Response) => {
    try {
      const media = await prisma.media.findUnique({
        where: { id: req.params.id as string },
      });

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

      const { title, description, altText } = req.body;
      const updateData: any = {};

      if (title !== undefined) {
        updateData.title = title || null;
        updateData.slug = title ? makeSlug(title) : media.slug;
      }
      if (description !== undefined) updateData.description = description || null;
      if (altText !== undefined) updateData.altText = altText || null;

      const updated = await prisma.media.update({
        where: { id: media.id },
        data: updateData,
      });

      res.json({ ...updated, url: `/media/${updated.id}` });
    } catch (err) {
      console.error('Update media error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

router.delete(
  '/:id',
  requireAuth,
  requires('gallery.delete'),
  async (req: Request, res: Response) => {
    try {
      const media = await prisma.media.findUnique({
        where: { id: req.params.id as string },
      });

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

      const filePath = path.join(STORAGE_PATH, media.id);
      const metaPath = path.join(STORAGE_PATH, `${media.id}.json`);

      if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
      if (fs.existsSync(metaPath)) fs.unlinkSync(metaPath);

      // Clean up any cached resized versions
      const cachePath = path.join(STORAGE_PATH, 'cache');
      if (fs.existsSync(cachePath)) {
        const cached = fs.readdirSync(cachePath)
          .filter((f) => f.startsWith(media.id));
        for (const f of cached) {
          fs.unlinkSync(path.join(cachePath, f));
        }
      }

      await prisma.media.delete({ where: { id: media.id } });

      res.json({ success: true });
    } catch (err) {
      console.error('Delete media error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

export default router;
