import fs from 'fs';
import { promises as fsp } from 'fs';
import path from 'path';
import { pipeline } from 'stream/promises';
import { Request, Response } from 'express';
import { config } from '../config';

const CHUNK_SIZE = 10 * 1024 * 1024; // 10MB

export function resolveVideoPath(videoId: string): string {
  const filePath = path.resolve(config.videosDir, videoId);
  // Path traversal guard
  if (!filePath.startsWith(path.resolve(config.videosDir))) {
    throw new Error('Invalid video path');
  }
  return filePath;
}

export async function streamVideo(req: Request, res: Response): Promise<void> {
  const { videoId } = req.params;

  let filePath: string;
  try {
    filePath = resolveVideoPath(videoId);
  } catch {
    res.status(400).json({ error: 'Invalid video ID' });
    return;
  }

  let stat: fs.Stats;
  try {
    stat = await fsp.stat(filePath);
  } catch {
    res.status(404).json({ error: 'Video not found' });
    return;
  }

  const fileSize = stat.size;
  const rangeHeader = req.headers.range;

  if (!rangeHeader) {
    res.writeHead(200, {
      'Content-Type': 'video/mp4',
      'Content-Length': fileSize,
      'Accept-Ranges': 'bytes',
    });
    const stream = fs.createReadStream(filePath, { highWaterMark: 1024 * 1024 });
    await pipeline(stream, res);
    return;
  }

  const [startStr, endStr] = rangeHeader.replace(/bytes=/, '').split('-');
  const start = parseInt(startStr, 10);
  const end = Math.min(
    endStr ? parseInt(endStr, 10) : start + CHUNK_SIZE - 1,
    fileSize - 1
  );

  if (start > end || start >= fileSize) {
    res.writeHead(416, { 'Content-Range': `bytes */${fileSize}` });
    res.end();
    return;
  }

  res.writeHead(206, {
    'Content-Range': `bytes ${start}-${end}/${fileSize}`,
    'Accept-Ranges': 'bytes',
    'Content-Length': end - start + 1,
    'Content-Type': 'video/mp4',
  });

  const stream = fs.createReadStream(filePath, {
    start,
    end,
    highWaterMark: 1024 * 1024,
  });
  await pipeline(stream, res);
}
