import { showAlert } from '../ui.js';
import { downloadFile, formatBytes } from '../utils/helpers.js';
import { PDFDocument, PDFName } from 'pdf-lib';
import { icons, createIcons } from 'lucide';

interface PageState {
    file: File | null;
    pdfDoc: PDFDocument | null;
}

const pageState: PageState = {
    file: null,
    pdfDoc: null,
};

function removeMetadataFromDoc(pdfDoc: PDFDocument) {
    const infoDict = (pdfDoc as any).getInfoDict();
    const allKeys = infoDict.keys();
    allKeys.forEach((key: any) => {
        infoDict.delete(key);
    });

    pdfDoc.setTitle('');
    pdfDoc.setAuthor('');
    pdfDoc.setSubject('');
    pdfDoc.setKeywords([]);
    pdfDoc.setCreator('');
    pdfDoc.setProducer('');

    try {
        const catalogDict = (pdfDoc.catalog as any).dict;
        if (catalogDict.has(PDFName.of('Metadata'))) {
            catalogDict.delete(PDFName.of('Metadata'));
        }
    } catch (e: any) {
        console.warn('Could not remove XMP metadata:', e.message);
    }

    try {
        const context = pdfDoc.context;
        if ((context as any).trailerInfo) {
            delete (context as any).trailerInfo.ID;
        }
    } catch (e: any) {
        console.warn('Could not remove document IDs:', e.message);
    }

    try {
        const catalogDict = (pdfDoc.catalog as any).dict;
        if (catalogDict.has(PDFName.of('PieceInfo'))) {
            catalogDict.delete(PDFName.of('PieceInfo'));
        }
    } catch (e: any) {
        console.warn('Could not remove PieceInfo:', e.message);
    }
}

function removeAnnotationsFromDoc(pdfDoc: PDFDocument) {
    const pages = pdfDoc.getPages();
    for (const page of pages) {
        try {
            page.node.delete(PDFName.of('Annots'));
        } catch (e: any) {
            console.warn('Could not remove annotations from page:', e.message);
        }
    }
}

function flattenFormsInDoc(pdfDoc: PDFDocument) {
    const form = pdfDoc.getForm();
    form.flatten();
}

function resetState() {
    pageState.file = null;
    pageState.pdfDoc = null;

    const fileDisplayArea = document.getElementById('file-display-area');
    if (fileDisplayArea) fileDisplayArea.innerHTML = '';

    const toolOptions = document.getElementById('tool-options');
    if (toolOptions) toolOptions.classList.add('hidden');

    const fileInput = document.getElementById('file-input') as HTMLInputElement;
    if (fileInput) fileInput.value = '';
}

async function updateUI() {
    const fileDisplayArea = document.getElementById('file-display-area');
    const toolOptions = document.getElementById('tool-options');

    if (!fileDisplayArea) return;

    fileDisplayArea.innerHTML = '';

    if (pageState.file) {
        const fileDiv = document.createElement('div');
        fileDiv.className = 'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm';

        const infoContainer = document.createElement('div');
        infoContainer.className = 'flex flex-col overflow-hidden';

        const nameSpan = document.createElement('div');
        nameSpan.className = 'truncate font-medium text-gray-200 text-sm mb-1';
        nameSpan.textContent = pageState.file.name;

        const metaSpan = document.createElement('div');
        metaSpan.className = 'text-xs text-gray-400';
        metaSpan.textContent = formatBytes(pageState.file.size);

        infoContainer.append(nameSpan, metaSpan);

        const removeBtn = document.createElement('button');
        removeBtn.className = 'ml-4 text-red-400 hover:text-red-300 flex-shrink-0';
        removeBtn.innerHTML = '<i data-lucide="trash-2" class="w-4 h-4"></i>';
        removeBtn.onclick = function () {
            resetState();
        };

        fileDiv.append(infoContainer, removeBtn);
        fileDisplayArea.appendChild(fileDiv);
        createIcons({ icons });

        if (toolOptions) toolOptions.classList.remove('hidden');
    } else {
        if (toolOptions) toolOptions.classList.add('hidden');
    }
}

async function handleFileSelect(files: FileList | null) {
    if (files && files.length > 0) {
        const file = files[0];
        if (file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')) {
            pageState.file = file;

            // Load PDF for sanitization
            try {
                const arrayBuffer = await file.arrayBuffer();
                pageState.pdfDoc = await PDFDocument.load(arrayBuffer);
                updateUI();
            } catch (e) {
                console.error('Error loading PDF:', e);
                showAlert('Error', 'Failed to load PDF file.');
            }
        }
    }
}

async function sanitizePdf() {
    if (!pageState.pdfDoc) {
        showAlert('Error', 'No PDF document loaded.');
        return;
    }

    const loaderModal = document.getElementById('loader-modal');
    const loaderText = document.getElementById('loader-text');
    if (loaderModal) loaderModal.classList.remove('hidden');
    if (loaderText) loaderText.textContent = 'Sanitizing PDF...';

    try {
        const pdfDoc = pageState.pdfDoc;

        const shouldFlattenForms = (document.getElementById('flatten-forms') as HTMLInputElement).checked;
        const shouldRemoveMetadata = (document.getElementById('remove-metadata') as HTMLInputElement).checked;
        const shouldRemoveAnnotations = (document.getElementById('remove-annotations') as HTMLInputElement).checked;
        const shouldRemoveJavascript = (document.getElementById('remove-javascript') as HTMLInputElement).checked;
        const shouldRemoveEmbeddedFiles = (document.getElementById('remove-embedded-files') as HTMLInputElement).checked;
        const shouldRemoveLayers = (document.getElementById('remove-layers') as HTMLInputElement).checked;
        const shouldRemoveLinks = (document.getElementById('remove-links') as HTMLInputElement).checked;
        const shouldRemoveStructureTree = (document.getElementById('remove-structure-tree') as HTMLInputElement).checked;
        const shouldRemoveMarkInfo = (document.getElementById('remove-markinfo') as HTMLInputElement).checked;
        const shouldRemoveFonts = (document.getElementById('remove-fonts') as HTMLInputElement).checked;

        let changesMade = false;

        if (shouldFlattenForms) {
            try {
                flattenFormsInDoc(pdfDoc);
                changesMade = true;
            } catch (e: any) {
                console.warn(`Could not flatten forms: ${e.message}`);
                try {
                    const catalogDict = (pdfDoc.catalog as any).dict;
                    if (catalogDict.has(PDFName.of('AcroForm'))) {
                        catalogDict.delete(PDFName.of('AcroForm'));
                        changesMade = true;
                    }
                } catch (removeError: any) {
                    console.warn('Could not remove AcroForm:', removeError.message);
                }
            }
        }

        if (shouldRemoveMetadata) {
            removeMetadataFromDoc(pdfDoc);
            changesMade = true;
        }

        if (shouldRemoveAnnotations) {
            removeAnnotationsFromDoc(pdfDoc);
            changesMade = true;
        }

        if (shouldRemoveJavascript) {
            try {
                if ((pdfDoc as any).javaScripts && (pdfDoc as any).javaScripts.length > 0) {
                    (pdfDoc as any).javaScripts = [];
                    changesMade = true;
                }

                const catalogDict = (pdfDoc.catalog as any).dict;

                const namesRef = catalogDict.get(PDFName.of('Names'));
                if (namesRef) {
                    try {
                        const namesDict = pdfDoc.context.lookup(namesRef) as any;
                        if (namesDict.has(PDFName.of('JavaScript'))) {
                            namesDict.delete(PDFName.of('JavaScript'));
                            changesMade = true;
                        }
                    } catch (e: any) {
                        console.warn('Could not access Names/JavaScript:', e.message);
                    }
                }

                if (catalogDict.has(PDFName.of('OpenAction'))) {
                    catalogDict.delete(PDFName.of('OpenAction'));
                    changesMade = true;
                }

                if (catalogDict.has(PDFName.of('AA'))) {
                    catalogDict.delete(PDFName.of('AA'));
                    changesMade = true;
                }

                const pages = pdfDoc.getPages();
                for (const page of pages) {
                    try {
                        const pageDict = page.node;

                        if (pageDict.has(PDFName.of('AA'))) {
                            pageDict.delete(PDFName.of('AA'));
                            changesMade = true;
                        }

                        const annotRefs = pageDict.Annots()?.asArray() || [];
                        for (const annotRef of annotRefs) {
                            try {
                                const annot = pdfDoc.context.lookup(annotRef) as any;

                                if (annot.has(PDFName.of('A'))) {
                                    const actionRef = annot.get(PDFName.of('A'));
                                    try {
                                        const actionDict = pdfDoc.context.lookup(actionRef) as any;
                                        const actionType = actionDict
                                            .get(PDFName.of('S'))
                                            ?.toString()
                                            .substring(1);

                                        if (actionType === 'JavaScript') {
                                            annot.delete(PDFName.of('A'));
                                            changesMade = true;
                                        }
                                    } catch (e: any) {
                                        console.warn('Could not read action:', e.message);
                                    }
                                }

                                if (annot.has(PDFName.of('AA'))) {
                                    annot.delete(PDFName.of('AA'));
                                    changesMade = true;
                                }
                            } catch (e: any) {
                                console.warn('Could not process annotation for JS:', e.message);
                            }
                        }
                    } catch (e: any) {
                        console.warn('Could not remove page actions:', e.message);
                    }
                }

                try {
                    const acroFormRef = catalogDict.get(PDFName.of('AcroForm'));
                    if (acroFormRef) {
                        const acroFormDict = pdfDoc.context.lookup(acroFormRef) as any;
                        const fieldsRef = acroFormDict.get(PDFName.of('Fields'));

                        if (fieldsRef) {
                            const fieldsArray = pdfDoc.context.lookup(fieldsRef) as any;
                            const fields = fieldsArray.asArray();

                            for (const fieldRef of fields) {
                                try {
                                    const field = pdfDoc.context.lookup(fieldRef) as any;

                                    if (field.has(PDFName.of('A'))) {
                                        field.delete(PDFName.of('A'));
                                        changesMade = true;
                                    }

                                    if (field.has(PDFName.of('AA'))) {
                                        field.delete(PDFName.of('AA'));
                                        changesMade = true;
                                    }
                                } catch (e: any) {
                                    console.warn('Could not process field for JS:', e.message);
                                }
                            }
                        }
                    }
                } catch (e: any) {
                    console.warn('Could not process form fields for JS:', e.message);
                }
            } catch (e: any) {
                console.warn(`Could not remove JavaScript: ${e.message}`);
            }
        }

        if (shouldRemoveEmbeddedFiles) {
            try {
                const catalogDict = (pdfDoc.catalog as any).dict;

                const namesRef = catalogDict.get(PDFName.of('Names'));
                if (namesRef) {
                    try {
                        const namesDict = pdfDoc.context.lookup(namesRef) as any;
                        if (namesDict.has(PDFName.of('EmbeddedFiles'))) {
                            namesDict.delete(PDFName.of('EmbeddedFiles'));
                            changesMade = true;
                        }
                    } catch (e: any) {
                        console.warn('Could not access Names/EmbeddedFiles:', e.message);
                    }
                }

                if (catalogDict.has(PDFName.of('EmbeddedFiles'))) {
                    catalogDict.delete(PDFName.of('EmbeddedFiles'));
                    changesMade = true;
                }

                const pages = pdfDoc.getPages();
                for (const page of pages) {
                    try {
                        const annotRefs = page.node.Annots()?.asArray() || [];
                        const annotsToKeep = [];

                        for (const ref of annotRefs) {
                            try {
                                const annot = pdfDoc.context.lookup(ref) as any;
                                const subtype = annot
                                    .get(PDFName.of('Subtype'))
                                    ?.toString()
                                    .substring(1);

                                if (subtype !== 'FileAttachment') {
                                    annotsToKeep.push(ref);
                                } else {
                                    changesMade = true;
                                }
                            } catch (e) {
                                annotsToKeep.push(ref);
                            }
                        }

                        if (annotsToKeep.length !== annotRefs.length) {
                            if (annotsToKeep.length > 0) {
                                const newAnnotsArray = pdfDoc.context.obj(annotsToKeep);
                                page.node.set(PDFName.of('Annots'), newAnnotsArray);
                            } else {
                                page.node.delete(PDFName.of('Annots'));
                            }
                        }
                    } catch (pageError: any) {
                        console.warn(
                            `Could not process page for attachments: ${pageError.message}`
                        );
                    }
                }

                if ((pdfDoc as any).embeddedFiles && (pdfDoc as any).embeddedFiles.length > 0) {
                    (pdfDoc as any).embeddedFiles = [];
                    changesMade = true;
                }

                if (catalogDict.has(PDFName.of('Collection'))) {
                    catalogDict.delete(PDFName.of('Collection'));
                    changesMade = true;
                }
            } catch (e: any) {
                console.warn(`Could not remove embedded files: ${e.message}`);
            }
        }

        if (shouldRemoveLayers) {
            try {
                const catalogDict = (pdfDoc.catalog as any).dict;

                if (catalogDict.has(PDFName.of('OCProperties'))) {
                    catalogDict.delete(PDFName.of('OCProperties'));
                    changesMade = true;
                }

                const pages = pdfDoc.getPages();
                for (const page of pages) {
                    try {
                        const pageDict = page.node;

                        if (pageDict.has(PDFName.of('OCProperties'))) {
                            pageDict.delete(PDFName.of('OCProperties'));
                            changesMade = true;
                        }

                        const resourcesRef = pageDict.get(PDFName.of('Resources'));
                        if (resourcesRef) {
                            try {
                                const resourcesDict = pdfDoc.context.lookup(resourcesRef) as any;
                                if (resourcesDict.has(PDFName.of('Properties'))) {
                                    resourcesDict.delete(PDFName.of('Properties'));
                                    changesMade = true;
                                }
                            } catch (e: any) {
                                console.warn('Could not access Resources:', e.message);
                            }
                        }
                    } catch (e: any) {
                        console.warn('Could not remove page layers:', e.message);
                    }
                }
            } catch (e: any) {
                console.warn(`Could not remove layers: ${e.message}`);
            }
        }

        if (shouldRemoveLinks) {
            try {
                const pages = pdfDoc.getPages();

                for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
                    try {
                        const page = pages[pageIndex];
                        const pageDict = page.node;

                        const annotsRef = pageDict.get(PDFName.of('Annots'));
                        if (!annotsRef) continue;

                        const annotsArray = pdfDoc.context.lookup(annotsRef) as any;
                        const annotRefs = annotsArray.asArray();

                        if (annotRefs.length === 0) continue;

                        const annotsToKeep = [];
                        let linksRemoved = 0;

                        for (const ref of annotRefs) {
                            try {
                                const annot = pdfDoc.context.lookup(ref) as any;
                                const subtype = annot
                                    .get(PDFName.of('Subtype'))
                                    ?.toString()
                                    .substring(1);

                                let isLink = false;

                                if (subtype === 'Link') {
                                    isLink = true;
                                    linksRemoved++;
                                } else {
                                    const actionRef = annot.get(PDFName.of('A'));
                                    if (actionRef) {
                                        try {
                                            const actionDict = pdfDoc.context.lookup(actionRef) as any;
                                            const actionType = actionDict
                                                .get(PDFName.of('S'))
                                                ?.toString()
                                                .substring(1);

                                            if (
                                                actionType === 'URI' ||
                                                actionType === 'Launch' ||
                                                actionType === 'GoTo' ||
                                                actionType === 'GoToR'
                                            ) {
                                                isLink = true;
                                                linksRemoved++;
                                            }
                                        } catch (e: any) {
                                            console.warn('Could not read action:', e.message);
                                        }
                                    }

                                    const dest = annot.get(PDFName.of('Dest'));
                                    if (dest && !isLink) {
                                        isLink = true;
                                        linksRemoved++;
                                    }
                                }

                                if (!isLink) {
                                    annotsToKeep.push(ref);
                                }
                            } catch (e: any) {
                                console.warn('Could not process annotation:', e.message);
                                annotsToKeep.push(ref);
                            }
                        }

                        if (linksRemoved > 0) {
                            if (annotsToKeep.length > 0) {
                                const newAnnotsArray = pdfDoc.context.obj(annotsToKeep);
                                pageDict.set(PDFName.of('Annots'), newAnnotsArray);
                            } else {
                                pageDict.delete(PDFName.of('Annots'));
                            }
                            changesMade = true;
                        }
                    } catch (pageError: any) {
                        console.warn(
                            `Could not process page ${pageIndex + 1} for links: ${pageError.message}`
                        );
                    }
                }

                try {
                    const catalogDict = (pdfDoc.catalog as any).dict;
                    const namesRef = catalogDict.get(PDFName.of('Names'));
                    if (namesRef) {
                        try {
                            const namesDict = pdfDoc.context.lookup(namesRef) as any;
                            if (namesDict.has(PDFName.of('Dests'))) {
                                namesDict.delete(PDFName.of('Dests'));
                                changesMade = true;
                            }
                        } catch (e: any) {
                            console.warn('Could not access Names/Dests:', e.message);
                        }
                    }

                    if (catalogDict.has(PDFName.of('Dests'))) {
                        catalogDict.delete(PDFName.of('Dests'));
                        changesMade = true;
                    }
                } catch (e: any) {
                    console.warn('Could not remove named destinations:', e.message);
                }
            } catch (e: any) {
                console.warn(`Could not remove links: ${e.message}`);
            }
        }

        if (shouldRemoveStructureTree) {
            try {
                const catalogDict = (pdfDoc.catalog as any).dict;

                if (catalogDict.has(PDFName.of('StructTreeRoot'))) {
                    catalogDict.delete(PDFName.of('StructTreeRoot'));
                    changesMade = true;
                }

                const pages = pdfDoc.getPages();
                for (const page of pages) {
                    try {
                        const pageDict = page.node;
                        if (pageDict.has(PDFName.of('StructParents'))) {
                            pageDict.delete(PDFName.of('StructParents'));
                            changesMade = true;
                        }
                    } catch (e: any) {
                        console.warn('Could not remove page StructParents:', e.message);
                    }
                }

                if (catalogDict.has(PDFName.of('ParentTree'))) {
                    catalogDict.delete(PDFName.of('ParentTree'));
                    changesMade = true;
                }
            } catch (e: any) {
                console.warn(`Could not remove structure tree: ${e.message}`);
            }
        }

        if (shouldRemoveMarkInfo) {
            try {
                const catalogDict = (pdfDoc.catalog as any).dict;

                if (catalogDict.has(PDFName.of('MarkInfo'))) {
                    catalogDict.delete(PDFName.of('MarkInfo'));
                    changesMade = true;
                }

                if (catalogDict.has(PDFName.of('Marked'))) {
                    catalogDict.delete(PDFName.of('Marked'));
                    changesMade = true;
                }
            } catch (e: any) {
                console.warn(`Could not remove MarkInfo: ${e.message}`);
            }
        }

        if (shouldRemoveFonts) {
            try {
                const pages = pdfDoc.getPages();

                for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
                    try {
                        const page = pages[pageIndex];
                        const pageDict = page.node;
                        const resourcesRef = pageDict.get(PDFName.of('Resources'));

                        if (resourcesRef) {
                            try {
                                const resourcesDict = pdfDoc.context.lookup(resourcesRef) as any;

                                if (resourcesDict.has(PDFName.of('Font'))) {
                                    const fontRef = resourcesDict.get(PDFName.of('Font'));

                                    try {
                                        const fontDict = pdfDoc.context.lookup(fontRef) as any;
                                        const fontKeys = fontDict.keys();

                                        for (const fontKey of fontKeys) {
                                            try {
                                                const specificFontRef = fontDict.get(fontKey);
                                                const specificFont =
                                                    pdfDoc.context.lookup(specificFontRef) as any;

                                                if (specificFont.has(PDFName.of('FontDescriptor'))) {
                                                    const descriptorRef = specificFont.get(
                                                        PDFName.of('FontDescriptor')
                                                    );
                                                    const descriptor =
                                                        pdfDoc.context.lookup(descriptorRef) as any;

                                                    const fontFileKeys = [
                                                        'FontFile',
                                                        'FontFile2',
                                                        'FontFile3',
                                                    ];
                                                    for (const key of fontFileKeys) {
                                                        if (descriptor.has(PDFName.of(key))) {
                                                            descriptor.delete(PDFName.of(key));
                                                            changesMade = true;
                                                        }
                                                    }
                                                }
                                            } catch (e: any) {
                                                console.warn(
                                                    `Could not process font ${fontKey}:`,
                                                    e.message
                                                );
                                            }
                                        }
                                    } catch (e: any) {
                                        console.warn(
                                            'Could not access font dictionary:',
                                            e.message
                                        );
                                    }
                                }
                            } catch (e: any) {
                                console.warn(
                                    'Could not access Resources for fonts:',
                                    e.message
                                );
                            }
                        }
                    } catch (e: any) {
                        console.warn(
                            `Could not remove fonts from page ${pageIndex + 1}:`,
                            e.message
                        );
                    }
                }

                if ((pdfDoc as any).fonts && (pdfDoc as any).fonts.length > 0) {
                    (pdfDoc as any).fonts = [];
                    changesMade = true;
                }
            } catch (e: any) {
                console.warn(`Could not remove fonts: ${e.message}`);
            }
        }

        if (!changesMade) {
            showAlert(
                'No Changes',
                'No items were selected for removal or none were found in the PDF.'
            );
            if (loaderModal) loaderModal.classList.add('hidden');
            return;
        }

        const sanitizedPdfBytes = await pdfDoc.save();
        downloadFile(
            new Blob([sanitizedPdfBytes as BlobPart], { type: 'application/pdf' }),
            'sanitized.pdf'
        );
        showAlert('Success', 'PDF has been sanitized and downloaded.', 'success', () => { resetState(); });
    } catch (e: any) {
        console.error('Sanitization Error:', e);
        showAlert('Error', `An error occurred during sanitization: ${e.message}`);
    } finally {
        if (loaderModal) loaderModal.classList.add('hidden');
    }
}

document.addEventListener('DOMContentLoaded', function () {
    const fileInput = document.getElementById('file-input') as HTMLInputElement;
    const dropZone = document.getElementById('drop-zone');
    const processBtn = document.getElementById('process-btn');
    const backBtn = document.getElementById('back-to-tools');

    if (backBtn) {
        backBtn.addEventListener('click', function () {
            window.location.href = import.meta.env.BASE_URL;
        });
    }

    if (fileInput && dropZone) {
        fileInput.addEventListener('change', function (e) {
            handleFileSelect((e.target as HTMLInputElement).files);
        });

        dropZone.addEventListener('dragover', function (e) {
            e.preventDefault();
            dropZone.classList.add('bg-gray-700');
        });

        dropZone.addEventListener('dragleave', function (e) {
            e.preventDefault();
            dropZone.classList.remove('bg-gray-700');
        });

        dropZone.addEventListener('drop', function (e) {
            e.preventDefault();
            dropZone.classList.remove('bg-gray-700');
            handleFileSelect(e.dataTransfer?.files);
        });

        fileInput.addEventListener('click', function () {
            fileInput.value = '';
        });
    }

    if (processBtn) {
        processBtn.addEventListener('click', sanitizePdf);
    }
});
