import { stringify } from 'csv-stringify/sync';
import { PDFDocument, PDFPage, rgb } from 'pdf-lib';

export interface ExportOptions {
  format: 'csv' | 'json' | 'pdf';
  filename: string;
  data: Record<string, any>[];
  columns?: string[];
  title?: string;
  includeTimestamp?: boolean;
}

export interface ExportResult {
  buffer: Buffer;
  mimeType: string;
  filename: string;
}

/**
 * Export data to CSV format
 */
export function exportToCSV(
  data: Record<string, any>[],
  columns?: string[],
  filename?: string
): ExportResult {
  if (data.length === 0) {
    throw new Error('No data to export');
  }

  // Determine columns from data if not provided
  const cols = columns || Object.keys(data[0]);

  // Create CSV string
  const csv = stringify(data, {
    header: true,
    columns: cols,
  });

  const buffer = Buffer.from(csv, 'utf-8');

  return {
    buffer,
    mimeType: 'text/csv',
    filename: filename || `export_${Date.now()}.csv`,
  };
}

/**
 * Export data to JSON format
 */
export function exportToJSON(
  data: Record<string, any>[],
  filename?: string,
  pretty = true
): ExportResult {
  if (data.length === 0) {
    throw new Error('No data to export');
  }

  const json = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
  const buffer = Buffer.from(json, 'utf-8');

  return {
    buffer,
    mimeType: 'application/json',
    filename: filename || `export_${Date.now()}.json`,
  };
}

/**
 * Export data to PDF format
 */
export async function exportToPDF(
  data: Record<string, any>[],
  options?: {
    filename?: string;
    title?: string;
    columns?: string[];
  }
): Promise<ExportResult> {
  if (data.length === 0) {
    throw new Error('No data to export');
  }

  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage([612, 792]); // Letter size
  const { height } = page.getSize();

  let yPosition = height - 50;
  const margin = 50;
  const lineHeight = 15;
  const columnWidth = (612 - 2 * margin) / Math.min(5, Object.keys(data[0]).length);

  // Add title
  if (options?.title) {
    page.drawText(options.title, {
      x: margin,
      y: yPosition,
      size: 16,
      color: rgb(0, 0, 0),
    });
    yPosition -= 30;
  }

  // Add timestamp
  page.drawText(`Generated: ${new Date().toLocaleString()}`, {
    x: margin,
    y: yPosition,
    size: 10,
    color: rgb(0.5, 0.5, 0.5),
  });
  yPosition -= 20;

  // Add headers
  const columns = options?.columns || Object.keys(data[0]);
  columns.forEach((col, index) => {
    page.drawText(col, {
      x: margin + index * columnWidth,
      y: yPosition,
      size: 11,
      color: rgb(0, 0, 0),
    });
  });

  yPosition -= lineHeight;

  // Add data rows
  for (const row of data) {
    if (yPosition < margin + lineHeight) {
      // Add new page if needed
      const newPage = pdfDoc.addPage([612, 792]);
      yPosition = height - margin;
    }

    columns.forEach((col, index) => {
      const value = String(row[col] || '');
      const truncated = value.length > 20 ? value.substring(0, 17) + '...' : value;

      newPage.drawText(truncated, {
        x: margin + index * columnWidth,
        y: yPosition,
        size: 10,
        color: rgb(0, 0, 0),
      });
    });

    yPosition -= lineHeight;
  }

  const pdfBytes = await pdfDoc.save();
  const buffer = Buffer.from(pdfBytes);

  return {
    buffer,
    mimeType: 'application/pdf',
    filename: options?.filename || `export_${Date.now()}.pdf`,
  };
}

/**
 * Generic export function that handles all formats
 */
export async function exportData(options: ExportOptions): Promise<ExportResult> {
  switch (options.format) {
    case 'csv':
      return exportToCSV(options.data, options.columns, options.filename);

    case 'json':
      return exportToJSON(options.data, options.filename);

    case 'pdf':
      return exportToPDF(options.data, {
        filename: options.filename,
        title: options.title,
        columns: options.columns,
      });

    default:
      throw new Error(`Unsupported export format: ${options.format}`);
  }
}

/**
 * Filter data based on criteria
 */
export function filterData(
  data: Record<string, any>[],
  filters: Record<string, any>
): Record<string, any>[] {
  return data.filter((row) => {
    return Object.entries(filters).every(([key, value]) => {
      if (value === null || value === undefined) return true;

      const rowValue = row[key];

      // Handle date range filters
      if (typeof value === 'object' && value.start && value.end) {
        const rowDate = new Date(rowValue).getTime();
        const startDate = new Date(value.start).getTime();
        const endDate = new Date(value.end).getTime();
        return rowDate >= startDate && rowDate <= endDate;
      }

      // Handle string search
      if (typeof value === 'string') {
        return String(rowValue).toLowerCase().includes(value.toLowerCase());
      }

      // Handle exact match
      return rowValue === value;
    });
  });
}

/**
 * Sort data based on column and direction
 */
export function sortData(
  data: Record<string, any>[],
  sortBy: string,
  direction: 'asc' | 'desc' = 'asc'
): Record<string, any>[] {
  return [...data].sort((a, b) => {
    const aValue = a[sortBy];
    const bValue = b[sortBy];

    if (aValue === null || aValue === undefined) return 1;
    if (bValue === null || bValue === undefined) return -1;

    let comparison = 0;

    if (typeof aValue === 'string') {
      comparison = aValue.localeCompare(bValue);
    } else if (typeof aValue === 'number') {
      comparison = aValue - bValue;
    } else if (aValue instanceof Date) {
      comparison = aValue.getTime() - bValue.getTime();
    }

    return direction === 'asc' ? comparison : -comparison;
  });
}

/**
 * Paginate data
 */
export function paginateData(
  data: Record<string, any>[],
  page: number,
  pageSize: number
): {
  data: Record<string, any>[];
  total: number;
  page: number;
  pageSize: number;
  totalPages: number;
} {
  const start = (page - 1) * pageSize;
  const end = start + pageSize;

  return {
    data: data.slice(start, end),
    total: data.length,
    page,
    pageSize,
    totalPages: Math.ceil(data.length / pageSize),
  };
}
