/**
 * Export Service
 *
 * Handles exporting comics to various formats:
 * - PDF (print-ready)
 * - CBZ (digital comic archive)
 * - PNG (individual pages)
 */

import { jsPDF } from 'jspdf';
import type { PageLayout, PanelImage } from '@/types';

export interface ExportOptions {
  format: 'pdf' | 'cbz' | 'png';
  pageSize: string;
  dpi: number;
  quality: number;
}

/**
 * Export comic to PDF format
 * Uses html2canvas to capture pages and jsPDF to generate PDF
 */
export async function exportToPDF(
  pageLayouts: PageLayout[],
  panelImages: Record<string, PanelImage>,
  options: ExportOptions
): Promise<Blob> {
  const pageSize = getPageSizeDimensions(options.pageSize);
  const isLandscape = pageSize.width > pageSize.height;

  const pdf = new jsPDF({
    orientation: isLandscape ? 'landscape' : 'portrait',
    unit: 'mm',
    format: options.pageSize.toLowerCase() as 'a4' | 'letter',
  });

  for (let i = 0; i < pageLayouts.length; i++) {
    if (i > 0) {
      pdf.addPage();
    }

    const pageLayout = pageLayouts[i];
    const canvas = await renderPageToCanvas(pageLayout, panelImages, options.dpi);
    
    if (!canvas) {
      console.warn(`Failed to render page ${i + 1}`);
      continue;
    }

    const imgData = canvas.toDataURL('image/png', options.quality / 100);
    const pdfWidth = pdf.internal.pageSize.getWidth();
    const pdfHeight = pdf.internal.pageSize.getHeight();

    pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
  }

  return pdf.output('blob');
}

/**
 * Export comic to CBZ format (ZIP archive of images)
 */
export async function exportToCBZ(
  pageLayouts: PageLayout[],
  panelImages: Record<string, PanelImage>,
  options: ExportOptions
): Promise<Blob> {
  const JSZip = await import('jszip');
  const zip = new JSZip.default();

  // Add each page as an image
  for (let i = 0; i < pageLayouts.length; i++) {
    const pageLayout = pageLayouts[i];
    const canvas = await renderPageToCanvas(pageLayout, panelImages, options.dpi);
    
    if (!canvas) {
      console.warn(`Failed to render page ${i + 1}`);
      continue;
    }

    const blob = await new Promise<Blob>((resolve) => {
      canvas.toBlob((b) => resolve(b!), 'image/jpeg', options.quality / 100);
    });

    // Pad page number: 001, 002, etc.
    const pageNum = String(i + 1).padStart(3, '0');
    zip.file(`page_${pageNum}.jpg`, blob);
  }

  // Add ComicInfo.xml metadata
  const comicInfo = generateComicInfoXML(pageLayouts);
  zip.file('ComicInfo.xml', comicInfo);

  return await zip.generateAsync({ type: 'blob' });
}

/**
 * Export individual pages as PNG images
 * Returns a ZIP containing all pages as PNG
 */
export async function exportToPNG(
  pageLayouts: PageLayout[],
  panelImages: Record<string, PanelImage>,
  options: ExportOptions
): Promise<Blob> {
  const JSZip = await import('jszip');
  const zip = new JSZip.default();

  for (let i = 0; i < pageLayouts.length; i++) {
    const pageLayout = pageLayouts[i];
    const canvas = await renderPageToCanvas(pageLayout, panelImages, options.dpi);
    
    if (!canvas) {
      console.warn(`Failed to render page ${i + 1}`);
      continue;
    }

    const blob = await new Promise<Blob>((resolve) => {
      canvas.toBlob((b) => resolve(b!), 'image/png');
    });

    const pageNum = String(i + 1).padStart(3, '0');
    zip.file(`page_${pageNum}.png`, blob);
  }

  return await zip.generateAsync({ type: 'blob' });
}

/**
 * Render a page layout to a canvas element
 * Combines panel images into the page layout
 */
async function renderPageToCanvas(
  pageLayout: PageLayout,
  panelImages: Record<string, PanelImage>,
  dpi: number
): Promise<HTMLCanvasElement | null> {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  
  if (!ctx) return null;

  // Set canvas size based on page dimensions and DPI
  const scale = dpi / 300; // Base scale on 300 DPI
  canvas.width = pageLayout.width * scale;
  canvas.height = pageLayout.height * scale;

  // Fill white background
  ctx.fillStyle = '#ffffff';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Render each panel
  for (const panel of pageLayout.panels) {
    const panelImage = panelImages[panel.panelId];
    if (!panelImage?.url) continue;

    try {
      const img = await loadImage(panelImage.url);
      
      // Calculate panel position and size
      const x = panel.layoutCell.x * canvas.width;
      const y = panel.layoutCell.y * canvas.height;
      const w = panel.layoutCell.w * canvas.width;
      const h = panel.layoutCell.h * canvas.height;

      // Draw panel image with gutter margins
      const gutter = 4 * scale;
      ctx.drawImage(
        img,
        x + gutter,
        y + gutter,
        w - gutter * 2,
        h - gutter * 2
      );

      // Draw panel border
      ctx.strokeStyle = '#000000';
      ctx.lineWidth = 2 * scale;
      ctx.strokeRect(x + gutter, y + gutter, w - gutter * 2, h - gutter * 2);
    } catch (error) {
      console.error(`Failed to load panel image ${panel.panelId}:`, error);
    }
  }

  return canvas;
}

/**
 * Load an image from URL
 */
function loadImage(url: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = url;
  });
}

/**
 * Generate ComicInfo.xml metadata for CBZ files
 * Standard format used by comic readers
 */
function generateComicInfoXML(pageLayouts: PageLayout[]): string {
  return `<?xml version="1.0" encoding="utf-8"?>
<ComicInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Title>Generated Comic</Title>
  <PageCount>${pageLayouts.length}</PageCount>
  <Pages>
    ${pageLayouts.map((_, index) => `    <Page Image="${index}" ImageSize="0" />`).join('\n')}
  </Pages>
</ComicInfo>`;
}

/**
 * Get page dimensions in pixels at specified DPI
 */
function getPageSizeDimensions(pageSize: string): { width: number; height: number } {
  const sizes: Record<string, { width: number; height: number }> = {
    a4: { width: 2480, height: 3508 },
    letter: { width: 2550, height: 3300 },
    manga: { width: 2158, height: 3035 },
    webtoon: { width: 800, height: 1280 },
    square: { width: 1080, height: 1080 },
  };

  return sizes[pageSize] || sizes.a4;
}

/**
 * Trigger file download
 */
export function downloadBlob(blob: Blob, filename: string): void {
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url);
}

/**
 * Export with progress tracking
 */
export async function exportWithProgress(
  pageLayouts: PageLayout[],
  panelImages: Record<string, PanelImage>,
  options: ExportOptions,
  onProgress?: (progress: number) => void
): Promise<Blob> {
  const totalSteps = pageLayouts.length + 1; // +1 for final processing
  let currentStep = 0;

  // Wrapper function to track progress
  const trackProgress = async <T,>(fn: () => Promise<T>): Promise<T> => {
    const result = await fn();
    currentStep++;
    onProgress?.(Math.min((currentStep / totalSteps) * 100, 100));
    return result;
  };

  switch (options.format) {
    case 'pdf':
      return trackProgress(() => exportToPDF(pageLayouts, panelImages, options));
    case 'cbz':
      return trackProgress(() => exportToCBZ(pageLayouts, panelImages, options));
    case 'png':
      return trackProgress(() => exportToPNG(pageLayouts, panelImages, options));
    default:
      throw new Error(`Unknown export format: ${options.format}`);
  }
}