import { Hono } from 'hono';
import { db, dbGet, dbAll, media } from '../db/index.js';
import { eq, and } from 'drizzle-orm';
import { requireAuth } from '../lib/auth.js';
import { generateId, getNow } from '../lib/utils.js';
import { getStorage, keyFromUrl } from '../lib/storage.js';

const mediaRouter = new Hono();

const MAX_FILE_SIZE =
  (Number(process.env.MEDIA_MAX_UPLOAD_MB || '10') || 10) * 1024 * 1024; // default 10MB

/**
 * Detect a real image type from the file's magic bytes (content sniffing).
 * Returns the canonical mime + extension, or null if the content is not an
 * allowed image. We deliberately ignore the client-supplied filename and
 * Content-Type so an attacker cannot store e.g. an .html/.svg payload.
 */
function detectImageType(buf: Buffer): { mime: string; ext: string } | null {
  if (buf.length < 12) return null;

  // JPEG: FF D8 FF
  if (buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {
    return { mime: 'image/jpeg', ext: '.jpg' };
  }
  // PNG: 89 50 4E 47 0D 0A 1A 0A
  if (
    buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47 &&
    buf[4] === 0x0d && buf[5] === 0x0a && buf[6] === 0x1a && buf[7] === 0x0a
  ) {
    return { mime: 'image/png', ext: '.png' };
  }
  // GIF: "GIF87a" / "GIF89a"
  if (buf.toString('ascii', 0, 6) === 'GIF87a' || buf.toString('ascii', 0, 6) === 'GIF89a') {
    return { mime: 'image/gif', ext: '.gif' };
  }
  // WEBP: "RIFF"...."WEBP"
  if (buf.toString('ascii', 0, 4) === 'RIFF' && buf.toString('ascii', 8, 12) === 'WEBP') {
    return { mime: 'image/webp', ext: '.webp' };
  }
  // AVIF / HEIF: "....ftyp" with an avif/heic brand
  if (buf.toString('ascii', 4, 8) === 'ftyp') {
    const brand = buf.toString('ascii', 8, 12);
    if (brand === 'avif' || brand === 'avis') {
      return { mime: 'image/avif', ext: '.avif' };
    }
  }
  return null;
}

// Upload image
mediaRouter.post('/upload', requireAuth(['admin', 'organizer']), async (c) => {
  try {
    const body = await c.req.parseBody();
    const file = body['file'] as File;
    
    if (!file) {
      return c.json({ error: 'No file provided' }, 400);
    }
    
    // Validate file size (cheap check before reading the whole buffer)
    if (file.size > MAX_FILE_SIZE) {
      const mb = Math.round((MAX_FILE_SIZE / (1024 * 1024)) * 10) / 10;
      return c.json({ error: `File too large. Maximum size: ${mb}MB` }, 400);
    }
    
    // Read the bytes and validate the *content* (not the client-provided type/name)
    const arrayBuffer = await file.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);

    const detected = detectImageType(buffer);
    if (!detected) {
      return c.json({ error: 'Invalid file. Allowed: JPEG, PNG, GIF, WebP, AVIF' }, 400);
    }
    
    // Generate unique filename using the *detected* extension (ignore client filename)
    const id = generateId();
    const filename = `${id}${detected.ext}`;

    // Persist via the storage backend (local disk or S3-compatible object store).
    const storage = getStorage();
    await storage.put(filename, buffer, detected.mime);
    
    // Get related info from form data
    const relatedId = body['relatedId'] as string | undefined;
    const relatedType = body['relatedType'] as string | undefined;
    
    // Save to database
    const now = getNow();
    const mediaRecord = {
      id,
      fileUrl: storage.publicUrl(filename),
      type: 'image' as const,
      relatedId: relatedId || null,
      relatedType: relatedType || null,
      createdAt: now,
    };
    
    await (db as any).insert(media).values(mediaRecord);
    
    return c.json({ 
      media: mediaRecord,
      url: mediaRecord.fileUrl,
    }, 201);
  } catch (error) {
    console.error('Upload error:', error);
    return c.json({ error: 'Failed to upload file' }, 500);
  }
});

// Get media by ID
mediaRouter.get('/:id', async (c) => {
  const id = c.req.param('id');
  
  const mediaRecord = await dbGet<any>(
    (db as any).select().from(media).where(eq((media as any).id, id))
  );
  
  if (!mediaRecord) {
    return c.json({ error: 'Media not found' }, 404);
  }
  
  return c.json({ media: mediaRecord });
});

// Delete media
mediaRouter.delete('/:id', requireAuth(['admin', 'organizer']), async (c) => {
  const id = c.req.param('id');
  
  const mediaRecord = await dbGet<any>(
    (db as any).select().from(media).where(eq((media as any).id, id))
  );
  
  if (!mediaRecord) {
    return c.json({ error: 'Media not found' }, 404);
  }
  
  // Delete the underlying object from the storage backend.
  try {
    await getStorage().delete(keyFromUrl(mediaRecord.fileUrl));
  } catch (error) {
    console.error('Failed to delete file:', error);
  }
  
  // Delete from database
  await (db as any).delete(media).where(eq((media as any).id, id));
  
  return c.json({ message: 'Media deleted successfully' });
});

// List media (admin)
mediaRouter.get('/', requireAuth(['admin', 'organizer']), async (c) => {
  const relatedType = c.req.query('relatedType');
  const relatedId = c.req.query('relatedId');
  const limit = Math.min(Math.max(parseInt(c.req.query('limit') || '200', 10) || 200, 1), 500);
  const offset = Math.max(parseInt(c.req.query('offset') || '0', 10) || 0, 0);
  
  // Combine filters into a single where() — chaining .where() replaces the prior condition in Drizzle.
  const conditions: any[] = [];
  if (relatedType) conditions.push(eq((media as any).relatedType, relatedType));
  if (relatedId) conditions.push(eq((media as any).relatedId, relatedId));

  let query = (db as any).select().from(media);
  if (conditions.length > 0) {
    query = query.where(conditions.length === 1 ? conditions[0] : and(...conditions));
  }
  
  const result = await dbAll(query.limit(limit).offset(offset));
  
  return c.json({ media: result });
});

export default mediaRouter;
