import jsPDF from 'jspdf';
import { ColumnDef, AccessorKeyColumnDef } from "@tanstack/react-table";

export interface TExportableData {
  [key: string]: string | number;
}

export interface TExportConfig {
    headers: string[];
    mappings: { [key: string]: string };
    filename: string;
}

export const getExportConfigFromColumns = <T extends TExportableData>(
    columns: ColumnDef<T, any>[],
    filename: string
): TExportConfig => {
    // Filter to only get columns with accessorKey and convert them to AccessorKeyColumnDef
    const exportableColumns = columns.filter((col): col is AccessorKeyColumnDef<T, any> => {
        if (col.id === "select") return false;
        return typeof (col as AccessorKeyColumnDef<T, any>).accessorKey === "string";
    });    const headers = exportableColumns.map((col) => {
        const accessorKey = String(col.accessorKey);

        if (typeof col.header === "string") {
            return col.header;
        }
        if (typeof col.header === "function") {
            // Try to get text content from the header if it's a function
            const headerContent = col.header({} as any);
            if (typeof headerContent === "string") {
                return headerContent;
            }
            // For complex header components, fallback to accessorKey
            return accessorKey.split("_")
                .map(word => word.charAt(0).toUpperCase() + word.slice(1))
                .join(" ");
        }
        // Fallback to formatted accessorKey if header is not defined
        return accessorKey.split("_")
            .map(word => word.charAt(0).toUpperCase() + word.slice(1))
            .join(" ");
    });    const mappings = exportableColumns.reduce((acc, col) => {
        const header = headers[exportableColumns.indexOf(col)];
        acc[header] = String(col.accessorKey);
        return acc;
    }, {} as { [key: string]: string });

    return {
        headers,
        mappings,
        filename
    };
};

export const downloadAsPDF = <T extends TExportableData>(data: T[], config: TExportConfig) => {
    if (data.length === 0) return;

    // Create PDF in landscape orientation with larger page size
    const doc = new jsPDF({
        orientation: 'landscape',
        unit: 'mm',
        format: 'a3'
    });

    const { headers, mappings, filename } = config;

    // Document settings
    const PAGE_MARGIN = 20;
    const PAGE_WIDTH = doc.internal.pageSize.width - 2 * PAGE_MARGIN;
    const PAGE_HEIGHT = doc.internal.pageSize.height;
    const LINE_HEIGHT = 10;
    const HEADER_Y = 25;
    const DATE_Y = 35;
    const TABLE_START_Y = 45;

    // Set initial font settings
    doc.setFont("helvetica", "bold");
    doc.setFontSize(20);
    doc.text(filename.replace('.pdf', ''), PAGE_MARGIN, HEADER_Y);

    // Add date
    doc.setFontSize(12);
    doc.text(`Generated on: ${new Date().toLocaleDateString()}`, PAGE_MARGIN, DATE_Y);

    // Function to measure text width
    const getTextWidth = (text: string) => {
        return doc.getStringUnitWidth(text) * doc.getFontSize() / doc.internal.scaleFactor;
    };

    // Calculate optimal column widths
    const columnWidths = headers.map(header => {
        // Start with header width
        let maxWidth = getTextWidth(header);

        // Check all values in this column
        data.forEach(row => {
            const field = mappings[header];
            const value = row[field]?.toString() || '';
            const valueWidth = getTextWidth(value);
            maxWidth = Math.max(maxWidth, valueWidth);
        });

        // Add padding
        return maxWidth + 10;
    });

    // Adjust widths if they exceed page width
    const totalWidth = columnWidths.reduce((sum, width) => sum + width, 0);
    const scaleFactor = totalWidth > (PAGE_WIDTH - 20) ? (PAGE_WIDTH - 20) / totalWidth : 1;
    const adjustedWidths = columnWidths.map(width => width * scaleFactor);

    // Start drawing from here
    let currentY = TABLE_START_Y;

    // Draw table header
    doc.setFillColor(240, 240, 240);
    doc.rect(PAGE_MARGIN, currentY - 5, PAGE_WIDTH, LINE_HEIGHT, 'F');

    doc.setFont("helvetica", "bold");
    let currentX = PAGE_MARGIN;
    headers.forEach((header, index) => {
        doc.text(header, currentX + 2, currentY);
        currentX += adjustedWidths[index];
    });

    currentY += LINE_HEIGHT;

    // Draw table grid lines
    doc.setDrawColor(200, 200, 200);
    doc.setLineWidth(0.1);

    // Draw data rows
    doc.setFont("helvetica", "normal");
    data.forEach((row, rowIndex) => {
        // Add zebra striping
        if (rowIndex % 2 === 0) {
            doc.setFillColor(249, 249, 249);
            doc.rect(PAGE_MARGIN, currentY - 5, PAGE_WIDTH, LINE_HEIGHT, 'F');
        }

        currentX = PAGE_MARGIN;
        headers.forEach((header, index) => {
            const field = mappings[header];
            const value = row[field]?.toString() || '';

            // Draw cell borders
            doc.rect(currentX, currentY - 5, adjustedWidths[index], LINE_HEIGHT);

            // Handle long text
            if (getTextWidth(value) > adjustedWidths[index] - 4) {
                const maxChars = Math.floor((adjustedWidths[index] - 4) * doc.internal.scaleFactor / doc.getFontSize() * 2);
                const truncatedValue = value.substring(0, maxChars) + '...';
                doc.text(truncatedValue, currentX + 2, currentY);
            } else {
                doc.text(value, currentX + 2, currentY);
            }

            currentX += adjustedWidths[index];
        });

        currentY += LINE_HEIGHT;

        // Add new page if needed
        if (currentY > PAGE_HEIGHT - PAGE_MARGIN) {
            doc.addPage();
            currentY = PAGE_MARGIN + LINE_HEIGHT;

            // Draw table header on new page
            doc.setFont("helvetica", "bold");
            doc.setFillColor(240, 240, 240);
            doc.rect(PAGE_MARGIN, currentY - 5, PAGE_WIDTH, LINE_HEIGHT, 'F');

            currentX = PAGE_MARGIN;
            headers.forEach((header, index) => {
                // Draw header cell border
                doc.rect(currentX, currentY - 5, adjustedWidths[index], LINE_HEIGHT);
                doc.text(header, currentX + 2, currentY);
                currentX += adjustedWidths[index];
            });

            currentY += LINE_HEIGHT;
            doc.setFont("helvetica", "normal");
        }
    });

    // Save the PDF
    doc.save(filename);
};

export const downloadAsCSV = <T extends TExportableData>(data: T[], config: TExportConfig) => {
    if (data.length === 0) return;

    const { headers, mappings, filename } = config;

    // Create CSV content
    const csvContent = [
        // Header row
        headers.join(','),
        // Data rows
        ...data.map(row =>
            headers.map(header => {
                const field = mappings[header];
                const value = row[field]?.toString() || '';
                // Escape quotes and wrap in quotes if contains comma
                return value.includes(',') ? `"${value.replace(/"/g, '""')}"` : value;
            }).join(',')
        )
    ].join('\n');

    // Create blob and download
    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement('a');
    if (link.download !== undefined) {
        const url = URL.createObjectURL(blob);
        link.setAttribute('href', url);
        link.setAttribute('download', filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
};
