/**
 * apply-template.ts
 * Apply the academic-paper figure container template to an extracted figure.
 *
 * Usage:
 *   npx -y bun apply-template.ts --figure figure.png --title "Title" --caption "Figure 1: Caption" --output slide.png
 *
 * Options:
 *   --figure   Path to extracted figure image (required)
 *   --title    Slide title/headline (required)
 *   --caption  Figure caption, e.g., "Figure 1: Description" (required)
 *   --output   Output slide PNG file path (required)
 *   --width    Output width, default 1920 (optional)
 *   --height   Output height, default 1080 (optional)
 */

import { existsSync, writeFileSync, mkdirSync } from "fs";
import { dirname, resolve } from "path";
import { createCanvas, loadImage, CanvasRenderingContext2D } from "canvas";

// Color palette from academic-paper style
const COLORS = {
  background: "#FFFFFF",
  titleText: "#1E3A5F",
  captionText: "#6B7280",
  border: "#E5E7EB",
  shadow: "rgba(0, 0, 0, 0.1)",
};

// Layout constants
const LAYOUT = {
  titleY: 80,
  titleFontSize: 48,
  captionFontSize: 24,
  figureMaxWidthRatio: 0.85,
  figureMaxHeightRatio: 0.65,
  padding: 20,
  borderRadius: 8,
  marginBottom: 80,
};

interface Args {
  figure: string;
  title: string;
  caption: string;
  output: string;
  width: number;
  height: number;
}

function parseArgs(): Args {
  const args = process.argv.slice(2);
  let figure = "";
  let title = "";
  let caption = "";
  let output = "";
  let width = 1920;
  let height = 1080;

  for (let i = 0; i < args.length; i++) {
    switch (args[i]) {
      case "--figure":
        figure = args[++i];
        break;
      case "--title":
        title = args[++i];
        break;
      case "--caption":
        caption = args[++i];
        break;
      case "--output":
        output = args[++i];
        break;
      case "--width":
        width = parseInt(args[++i], 10);
        break;
      case "--height":
        height = parseInt(args[++i], 10);
        break;
    }
  }

  if (!figure || !title || !caption || !output) {
    console.error("Usage: bun apply-template.ts --figure <image> --title <title> --caption <caption> --output <output.png>");
    console.error("\nOptions:");
    console.error("  --figure   Path to extracted figure image (required)");
    console.error("  --title    Slide title/headline (required)");
    console.error("  --caption  Figure caption (required)");
    console.error("  --output   Output slide PNG file path (required)");
    console.error("  --width    Output width, default 1920 (optional)");
    console.error("  --height   Output height, default 1080 (optional)");
    process.exit(1);
  }

  return { figure, title, caption, output, width, height };
}

function wrapText(ctx: CanvasRenderingContext2D, text: string, maxWidth: number): string[] {
  const words = text.split(" ");
  const lines: string[] = [];
  let currentLine = "";

  for (const word of words) {
    const testLine = currentLine ? `${currentLine} ${word}` : word;
    const metrics = ctx.measureText(testLine);

    if (metrics.width > maxWidth && currentLine) {
      lines.push(currentLine);
      currentLine = word;
    } else {
      currentLine = testLine;
    }
  }

  if (currentLine) {
    lines.push(currentLine);
  }

  return lines;
}

function drawRoundedRect(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number
) {
  ctx.beginPath();
  ctx.moveTo(x + radius, y);
  ctx.lineTo(x + width - radius, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  ctx.lineTo(x + width, y + height - radius);
  ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  ctx.lineTo(x + radius, y + height);
  ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
  ctx.lineTo(x, y + radius);
  ctx.quadraticCurveTo(x, y, x + radius, y);
  ctx.closePath();
}

async function applyTemplate(args: Args) {
  const { figure, title, caption, output, width, height } = args;

  // Validate figure exists
  const absoluteFigurePath = resolve(figure);
  if (!existsSync(absoluteFigurePath)) {
    throw new Error(`Figure image not found: ${absoluteFigurePath}`);
  }

  console.log(`Loading figure: ${absoluteFigurePath}`);

  // Load the figure image
  const figureImage = await loadImage(absoluteFigurePath);
  console.log(`Figure size: ${figureImage.width}x${figureImage.height}`);

  // Create output canvas
  const canvas = createCanvas(width, height);
  const ctx = canvas.getContext("2d");

  // Scale factors for different resolutions
  const scaleFactor = width / 1920;
  const titleFontSize = Math.round(LAYOUT.titleFontSize * scaleFactor);
  const captionFontSize = Math.round(LAYOUT.captionFontSize * scaleFactor);
  const padding = Math.round(LAYOUT.padding * scaleFactor);
  const borderRadius = Math.round(LAYOUT.borderRadius * scaleFactor);

  // Fill background
  ctx.fillStyle = COLORS.background;
  ctx.fillRect(0, 0, width, height);

  // Draw title
  ctx.fillStyle = COLORS.titleText;
  ctx.font = `bold ${titleFontSize}px Arial, Helvetica, sans-serif`;
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";

  const titleMaxWidth = width * 0.9;
  const titleLines = wrapText(ctx, title, titleMaxWidth);
  const titleLineHeight = titleFontSize * 1.3;
  const titleStartY = Math.round(LAYOUT.titleY * scaleFactor);

  titleLines.forEach((line, index) => {
    ctx.fillText(line, width / 2, titleStartY + index * titleLineHeight);
  });

  // Calculate figure area
  const titleBottomY = titleStartY + titleLines.length * titleLineHeight + padding * 2;
  const captionY = height - Math.round(LAYOUT.marginBottom * scaleFactor);
  const figureAreaHeight = captionY - titleBottomY - padding * 2;

  const maxFigureWidth = width * LAYOUT.figureMaxWidthRatio;
  const maxFigureHeight = Math.min(height * LAYOUT.figureMaxHeightRatio, figureAreaHeight);

  // Calculate scaled figure dimensions
  const figureAspect = figureImage.width / figureImage.height;
  let scaledWidth: number;
  let scaledHeight: number;

  if (figureAspect > maxFigureWidth / maxFigureHeight) {
    // Width constrained
    scaledWidth = maxFigureWidth;
    scaledHeight = scaledWidth / figureAspect;
  } else {
    // Height constrained
    scaledHeight = maxFigureHeight;
    scaledWidth = scaledHeight * figureAspect;
  }

  // Center figure horizontally and vertically in available space
  const figureX = (width - scaledWidth) / 2;
  const figureY = titleBottomY + (figureAreaHeight - scaledHeight) / 2;

  // Draw figure container with shadow and border
  const containerX = figureX - padding;
  const containerY = figureY - padding;
  const containerWidth = scaledWidth + padding * 2;
  const containerHeight = scaledHeight + padding * 2;

  // Shadow
  ctx.save();
  ctx.shadowColor = COLORS.shadow;
  ctx.shadowBlur = 12 * scaleFactor;
  ctx.shadowOffsetX = 0;
  ctx.shadowOffsetY = 4 * scaleFactor;
  ctx.fillStyle = COLORS.background;
  drawRoundedRect(ctx, containerX, containerY, containerWidth, containerHeight, borderRadius);
  ctx.fill();
  ctx.restore();

  // Border
  ctx.strokeStyle = COLORS.border;
  ctx.lineWidth = 1;
  drawRoundedRect(ctx, containerX, containerY, containerWidth, containerHeight, borderRadius);
  ctx.stroke();

  // Draw figure
  ctx.drawImage(figureImage, figureX, figureY, scaledWidth, scaledHeight);

  // Draw caption
  ctx.fillStyle = COLORS.captionText;
  ctx.font = `italic ${captionFontSize}px Arial, Helvetica, sans-serif`;
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";

  const captionMaxWidth = width * 0.85;
  const captionLines = wrapText(ctx, caption, captionMaxWidth);
  const captionLineHeight = captionFontSize * 1.4;

  captionLines.forEach((line, index) => {
    ctx.fillText(line, width / 2, captionY + index * captionLineHeight);
  });

  // Ensure output directory exists
  const outputDir = dirname(output);
  if (outputDir && !existsSync(outputDir)) {
    mkdirSync(outputDir, { recursive: true });
  }

  // Save as PNG
  const buffer = canvas.toBuffer("image/png");
  writeFileSync(output, buffer);

  console.log(`\nSaved: ${output}`);
  console.log(`Size: ${width}x${height}`);
  console.log(`File size: ${Math.round(buffer.length / 1024)} KB`);
}

async function main() {
  const args = parseArgs();

  try {
    await applyTemplate(args);
    console.log("\nTemplate applied successfully!");
  } catch (error) {
    console.error("\nError:", error instanceof Error ? error.message : error);
    process.exit(1);
  }
}

main();
