import express from 'express';
import cors from 'cors';
import crypto from 'crypto';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { spawn } from 'child_process';

// Generate a unique link ID
const generateUniqueId = () => {
  return crypto.randomBytes(8).toString('hex');
};

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

const app = express();
const PORT = process.env.PORT ? Number(process.env.PORT) : 3005;

// Middleware
app.use(cors());
app.use(express.json());

// Ensure media directory exists
const mediaDir = path.join(__dirname, '..', 'public', 'media');
if (!fs.existsSync(mediaDir)) {
  fs.mkdirSync(mediaDir, { recursive: true });
}

// Configure multer for video uploads
const getExtensionForMime = (mimetype) => {
  if (!mimetype) return '.webm';
  if (mimetype.includes('mp4')) return '.mp4';
  if (mimetype.includes('webm')) return '.webm';
  if (mimetype.includes('quicktime')) return '.mov';
  return '.webm';
};

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, mediaDir);
  },
  filename: (req, file, cb) => {
    const timestamp = Date.now();
    const ext = getExtensionForMime(file.mimetype);
    const safeName = `recording_${timestamp}${ext}`;
    cb(null, safeName);
  }
});

const upload = multer({
  storage,
  limits: { fileSize: 100 * 1024 * 1024 }, // 100MB limit
  fileFilter: (req, file, cb) => {
    if (file.mimetype.startsWith('video/')) {
      cb(null, true);
    } else {
      cb(new Error('Only video files are allowed'));
    }
  }
});

// Initialize SQLite database
let db;

const transcodeToMp4 = (inputPath, outputPath) => new Promise((resolve, reject) => {
  const args = [
    '-y',
    '-i', inputPath,
    '-vf', 'scale=trunc(iw/2)*2:trunc(ih/2)*2',
    '-c:v', 'libx264',
    '-profile:v', 'baseline',
    '-level', '3.0',
    '-pix_fmt', 'yuv420p',
    '-c:a', 'aac',
    '-b:a', '128k',
    '-movflags', '+faststart',
    outputPath
  ];

  const ffmpeg = spawn('ffmpeg', args, { stdio: 'ignore' });
  ffmpeg.on('error', reject);
  ffmpeg.on('close', (code) => {
    if (code === 0) {
      resolve();
    } else {
      reject(new Error(`ffmpeg exited with code ${code}`));
    }
  });
});

const isMp4Upload = (file) => {
  if (!file) return false;
  if (file.mimetype && file.mimetype.includes('mp4')) return true;
  return path.extname(file.filename).toLowerCase() === '.mp4';
};

const transcodeQueue = [];
let isTranscoding = false;

const processTranscodeQueue = async () => {
  if (isTranscoding) return;
  const job = transcodeQueue.shift();
  if (!job) return;
  isTranscoding = true;
  try {
    await job();
  } catch (error) {
    console.error('Transcode job failed:', error);
  } finally {
    isTranscoding = false;
    processTranscodeQueue();
  }
};

const enqueueTranscode = (job) => {
  transcodeQueue.push(job);
  processTranscodeQueue();
};

async function initializeDatabase() {
  db = await open({
    filename: path.join(__dirname, 'signsync.db'),
    driver: sqlite3.Database
  });

  await db.exec(`
    CREATE TABLE IF NOT EXISTS recordings (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      unique_link TEXT UNIQUE NOT NULL,
      name TEXT NOT NULL,
      email TEXT NOT NULL,
      recorded_video_path TEXT NOT NULL,
      recorded_video_processing INTEGER DEFAULT 0,
      youtube_video_url TEXT NOT NULL,
      name_2 TEXT,
      email_2 TEXT,
      recorded_video_path_2 TEXT,
      recorded_video_processing_2 INTEGER DEFAULT 0,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )
  `);

  const addColumnIfMissing = async (columnDef) => {
    try {
      await db.exec(`ALTER TABLE recordings ADD COLUMN ${columnDef}`);
    } catch (error) {
      if (!String(error.message).toLowerCase().includes('duplicate column')) {
        console.error('Error adding column:', error);
      }
    }
  };

  await addColumnIfMissing('recorded_video_processing INTEGER DEFAULT 0');
  await addColumnIfMissing('recorded_video_processing_2 INTEGER DEFAULT 0');

  await db.run('UPDATE recordings SET recorded_video_processing = 0 WHERE recorded_video_processing IS NULL');
  await db.run('UPDATE recordings SET recorded_video_processing_2 = 0 WHERE recorded_video_processing_2 IS NULL');

  console.log('Database initialized');
}

// API Routes

// Save recording with user info
app.post('/api/recordings', upload.single('video'), async (req, res) => {
  try {
    const { name, email, youtubeVideoUrl } = req.body;

    if (!name || !email || !req.file) {
      return res.status(400).json({
        error: 'Name, email, and video file are required'
      });
    }

    const recordedVideoPath = `/media/${req.file.filename}`;
    const uniqueLink = generateUniqueId();
    const needsTranscode = !isMp4Upload(req.file);
    const recordedVideoProcessing = needsTranscode ? 1 : 0;

    const result = await db.run(
      `INSERT INTO recordings (unique_link, name, email, recorded_video_path, recorded_video_processing, youtube_video_url)
       VALUES (?, ?, ?, ?, ?, ?)`,
      [uniqueLink, name, email, recordedVideoPath, recordedVideoProcessing, youtubeVideoUrl || '']
    );

    res.json({
      success: true,
      id: result.lastID,
      uniqueLink,
      recordedVideoPath,
      recordedVideoProcessing,
      message: 'Recording saved successfully'
    });

    if (needsTranscode) {
      const inputPath = req.file.path;
      const outputPath = inputPath.replace(/\.[^.]+$/, '.mp4');
      enqueueTranscode(async () => {
        try {
          await transcodeToMp4(inputPath, outputPath);
          await db.run(
            `UPDATE recordings SET recorded_video_path = ?, recorded_video_processing = 0 WHERE unique_link = ?`,
            [`/media/${path.basename(outputPath)}`, uniqueLink]
          );
          fs.unlink(inputPath, () => {});
        } catch (transcodeError) {
          console.error('Transcode failed, keeping original file:', transcodeError);
          await db.run(
            `UPDATE recordings SET recorded_video_processing = 0 WHERE unique_link = ?`,
            [uniqueLink]
          );
        }
      });
    }
  } catch (error) {
    console.error('Error saving recording:', error);
    res.status(500).json({ error: 'Failed to save recording' });
  }
});

// Get all recordings
app.get('/api/recordings', async (req, res) => {
  try {
    const recordings = await db.all(
      'SELECT * FROM recordings ORDER BY created_at DESC'
    );
    res.json(recordings);
  } catch (error) {
    console.error('Error fetching recordings:', error);
    res.status(500).json({ error: 'Failed to fetch recordings' });
  }
});

// Get recording by ID
app.get('/api/recordings/:id', async (req, res) => {
  try {
    const recording = await db.get(
      'SELECT * FROM recordings WHERE id = ?',
      [req.params.id]
    );

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

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

// Get recording by unique link
app.get('/api/share/:uniqueLink', async (req, res) => {
  try {
    const recording = await db.get(
      'SELECT * FROM recordings WHERE unique_link = ?',
      [req.params.uniqueLink]
    );

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

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

// Add second recording to an existing shared recording
app.post('/api/share/:uniqueLink/second-video', upload.single('video'), async (req, res) => {
  try {
    const { name, email } = req.body;
    const { uniqueLink } = req.params;

    if (!name || !email || !req.file) {
      return res.status(400).json({
        error: 'Name, email, and video file are required'
      });
    }

    // Check if recording exists
    const recording = await db.get(
      'SELECT * FROM recordings WHERE unique_link = ?',
      [uniqueLink]
    );

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

    if (recording.recorded_video_path_2) {
      return res.status(400).json({ error: 'Second video already recorded' });
    }

    const recordedVideoPath2 = `/media/${req.file.filename}`;
    const needsTranscode = !isMp4Upload(req.file);
    const recordedVideoProcessing2 = needsTranscode ? 1 : 0;

    await db.run(
      `UPDATE recordings
       SET name_2 = ?, email_2 = ?, recorded_video_path_2 = ?, recorded_video_processing_2 = ?
       WHERE unique_link = ?`,
      [name, email, recordedVideoPath2, recordedVideoProcessing2, uniqueLink]
    );

    res.json({
      success: true,
      recordedVideoPath2,
      recordedVideoProcessing2,
      message: 'Second recording saved successfully'
    });

    if (needsTranscode) {
      const inputPath = req.file.path;
      const outputPath = inputPath.replace(/\.[^.]+$/, '.mp4');
      enqueueTranscode(async () => {
        try {
          await transcodeToMp4(inputPath, outputPath);
          await db.run(
            `UPDATE recordings SET recorded_video_path_2 = ?, recorded_video_processing_2 = 0 WHERE unique_link = ?`,
            [`/media/${path.basename(outputPath)}`, uniqueLink]
          );
          fs.unlink(inputPath, () => {});
        } catch (transcodeError) {
          console.error('Transcode failed, keeping original file:', transcodeError);
          await db.run(
            `UPDATE recordings SET recorded_video_processing_2 = 0 WHERE unique_link = ?`,
            [uniqueLink]
          );
        }
      });
    }
  } catch (error) {
    console.error('Error saving second recording:', error);
    res.status(500).json({ error: 'Failed to save second recording' });
  }
});

// Start server
initializeDatabase().then(() => {
  app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
  });
});
