// pages/api/send.ts
import { prisma } from '@/lib/prisma';
import { UPLOAD_DIR } from '@/lib/config';
import { PLAN_CONFIG } from '@/lib/subscription';
import { sendMailjetEmail } from '@/lib/mailjet';
import { v4 as uuidv4 } from 'uuid';
import bcrypt from 'bcrypt';
import path from 'path';
import { IncomingForm, File as FormidableFile } from 'formidable';
import fs from 'fs';
import { promises as fsPromises } from 'fs';
import { NextApiRequest, NextApiResponse } from 'next';
import { getServerSession } from 'next-auth/next';
import { authOptions } from '@/lib/auth';

export const config = {
  api: {
    bodyParser: false,
  },
};

interface ParsedFields {
  [key: string]: string | string[] | undefined;
}

interface ParsedFiles {
  [key: string]: FormidableFile | FormidableFile[] | undefined;
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ success: false, message: 'Method not allowed' });
  }

  const session = await getServerSession(req, res, authOptions);
  if (!session?.user?.email) {
    return res.status(401).json({ success: false, message: 'Unauthorized' });
  }

  const form = new IncomingForm({
    uploadDir: UPLOAD_DIR,
    keepExtensions: true,
    maxFileSize: 100 * 1024 * 1024 * 1024, // 100GB
  });

  const parseForm = (): Promise<{ fields: ParsedFields; files: ParsedFiles }> =>
    new Promise((resolve, reject) => {
      form.parse(req, (err: Error | null, fields: any, files: any) => {
        if (err) return reject(err);
        resolve({ fields, files });
      });
    });

  try {
    const { fields, files } = await parseForm();

    // Extract form fields
    const encryptedFile = Array.isArray(files.file) ? files.file[0] : files.file;
    const recipient = Array.isArray(fields.email) ? fields.email[0] : fields.email;
    const password = Array.isArray(fields.password) ? fields.password[0] : fields.password;
    // Sender is taken from the session, never the request body, so a caller
    // cannot post transfers (and consume quota) as another user.
    const sender = session.user.email;
    const filenames = Array.isArray(fields.filenames) ? fields.filenames[0] : fields.filenames;
    const message = Array.isArray(fields.message) ? fields.message[0] : fields.message;
    const encryptionIvStr = Array.isArray(fields.encryptionIv) ? fields.encryptionIv[0] : fields.encryptionIv;
    const originalFilename = Array.isArray(fields.originalFilename) ? fields.originalFilename[0] : fields.originalFilename;

    if (!encryptedFile || !recipient || !sender) {
      return res.status(400).json({ success: false, message: 'Missing required fields' });
    }

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

    // Generate final filename and move encrypted file
    const finalFilename = uuidv4() + '.enc';
    const finalPath = path.join(UPLOAD_DIR, finalFilename);
    
    // Use streaming copy for large files to avoid permission issues
    await new Promise<void>((resolve, reject) => {
      const readStream = fs.createReadStream(encryptedFile.filepath);
      const writeStream = fs.createWriteStream(finalPath);
      
      readStream.on('error', reject);
      writeStream.on('error', reject);
      writeStream.on('finish', resolve);
      
      readStream.pipe(writeStream);
    });
    
    // Clean up temp file after successful copy
    try {
      await fsPromises.unlink(encryptedFile.filepath);
    } catch (err) {
      console.warn('Failed to clean up temp file:', err);
    }

    // Verify user exists
    const user = await prisma.user.findUnique({ where: { email: sender } });
    if (!user) {
      fs.unlinkSync(finalPath);
      return res.status(404).json({ success: false, message: 'User not found' });
    }

    const plan = (user.plan || 'free') as 'free' | 'rookie' | 'pro';
    const limits = PLAN_CONFIG[plan];

    // Check file size against plan limits
    const stats = fs.statSync(finalPath);
    if (limits.maxFileSize !== Infinity && stats.size > limits.maxFileSize) {
      fs.unlinkSync(finalPath);
      return res.status(413).json({ 
        success: false, 
        message: `File exceeds max size for your plan (${plan})` 
      });
    }

    // Check monthly transfer limits
    const startOfMonth = new Date();
    startOfMonth.setDate(1);
    startOfMonth.setHours(0, 0, 0, 0);

    const transfersThisMonth = await prisma.transfer.findMany({
      where: {
        senderEmail: sender,
        createdAt: { gte: startOfMonth },
      },
    });

    const totalSizeSentThisMonth = transfersThisMonth.reduce((acc, t) => acc + BigInt(t.totalSize), BigInt(0));
    if (
      limits.maxTransfersPerMonth !== Infinity && transfersThisMonth.length >= limits.maxTransfersPerMonth ||
      limits.maxTransferSizePerMonth !== Infinity && totalSizeSentThisMonth + BigInt(stats.size) > BigInt(limits.maxTransferSizePerMonth)
    ) {
      fs.unlinkSync(finalPath);
      return res.status(429).json({ success: false, message: 'Plan limits exceeded' });
    }

    // Hash password if provided
    const hash = password ? await bcrypt.hash(password, 10) : null;

    // Parse filenames if provided
    let totalFiles = 1;
    if (filenames) {
      try {
        const filesArray = JSON.parse(filenames);
        if (Array.isArray(filesArray)) {
          totalFiles = filesArray.length;
        }
      } catch {}
    }

    const expiresAt = new Date(Date.now() + limits.maxExpiryMs);

    // Create transfer record with encrypted file
    const transfer = await prisma.transfer.create({
      data: {
        senderEmail: sender,
        recipientEmail: recipient,
        passwordHash: hash,
        downloadUrl: uuidv4(),
        expiresAt,
        filenames: filenames || null,
        message: message || null,
        totalFiles,
        totalSize: stats.size,
        userId: user?.id,
        files: {
          create: [
            {
              name: originalFilename || 'file',
              path: finalPath,
              size: stats.size,
            },
          ],
        },
      },
    });

    // Update file with encryption IV using raw query
    if (encryptionIvStr) {
      await (prisma as any).$executeRaw`
        UPDATE "File" SET "encryptionIv" = ${encryptionIvStr}
        WHERE "transferId" = ${transfer.id}
      `;
    }

    // Send email notification
    const link = `${process.env.NEXT_PUBLIC_APP_URL}/download/${transfer.downloadUrl}`;

    await sendMailjetEmail({
      to: recipient,
      subject: 'You\'ve received an encrypted file',
      text: `
${sender} sent you an encrypted file via TransferTribe.

Link: ${link}

${message ? `Message:\n${message}\n\n` : ''}Note: You'll need the password they shared with you to decrypt it.
`.trim(),
    });

    return res.status(200).json({ success: true });
  } catch (err: any) {
    console.error('Upload failed:', err);
    return res.status(500).json({ success: false, message: 'Upload failed: ' + err.message });
  }
}
