import 'bootstrap';
import './styles/sign.scss';

declare global {
    interface Window {
        pdfjsLib?: {
            getDocument: (src: { url: string; withCredentials?: boolean }) => {
                promise: Promise<{
                    numPages: number;
                    getPage: (pageNumber: number) => Promise<{
                        getViewport: (params: { scale: number }) => { width: number; height: number };
                        render: (params: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }) => { promise: Promise<void> };
                    }>;
                }>;
            };
            GlobalWorkerOptions: { workerSrc: string };
        };
    }
}

type SignFieldData = {
    id: number;
    type: string;
    page: number;
    posX: number;
    posY: number;
    width: number;
    height: number;
    color: string;
    label: string;
    signerName: string;
    value?: string | null;
    completed?: boolean;
};

document.addEventListener('DOMContentLoaded', () => {
    const dropzone = document.getElementById('signDropzone');
    const input = document.getElementById('signPdfInput') as HTMLInputElement | null;
    const selectBtn = document.getElementById('signSelectPdfBtn');

    const addSignerModal = document.getElementById('addSignerModal');
    if (addSignerModal && addSignerModal.parentElement !== document.body) {
        document.body.appendChild(addSignerModal);
    }

    if (dropzone && input) {
        const openPicker = () => input.click();
        selectBtn?.addEventListener('click', openPicker);
        dropzone.addEventListener('click', (event) => {
            const target = event.target as HTMLElement;
            if (target.closest('button') || target === input) {
                return;
            }
            openPicker();
        });

        input.addEventListener('change', () => {
            if (input.files && input.files.length > 0) {
                (dropzone as HTMLFormElement).submit();
            }
        });

        ['dragenter', 'dragover'].forEach((eventName) => {
            dropzone.addEventListener(eventName, (event) => {
                event.preventDefault();
                event.stopPropagation();
                dropzone.classList.add('is-dragover');
            });
        });

        ['dragleave', 'drop'].forEach((eventName) => {
            dropzone.addEventListener(eventName, (event) => {
                event.preventDefault();
                event.stopPropagation();
                dropzone.classList.remove('is-dragover');
            });
        });

        dropzone.addEventListener('drop', (event: DragEvent) => {
            const files = event.dataTransfer?.files;
            if (!files || files.length === 0) {
                return;
            }
            const file = files[0];
            if (file.type !== 'application/pdf' && !file.name.toLowerCase().endsWith('.pdf')) {
                alert('Nur PDF-Dateien werden unterstützt.');
                return;
            }
            const dt = new DataTransfer();
            dt.items.add(file);
            input.files = dt.files;
            (dropzone as HTMLFormElement).submit();
        });
    }

    const contactPick = document.getElementById('contactPick') as HTMLSelectElement | null;
    if (contactPick) {
        contactPick.addEventListener('change', () => {
            const option = contactPick.selectedOptions[0];
            const email = document.getElementById('email') as HTMLInputElement | null;
            const firstName = document.getElementById('firstName') as HTMLInputElement | null;
            const lastName = document.getElementById('lastName') as HTMLInputElement | null;
            const cellphone = document.getElementById('cellphone') as HTMLInputElement | null;
            if (!option || !option.value) {
                return;
            }
            if (email) email.value = option.value;
            if (firstName) firstName.value = option.dataset.first || '';
            if (lastName) lastName.value = option.dataset.last || '';
            if (cellphone) cellphone.value = option.dataset.phone || '';
        });
    }

    const modeRadios = document.querySelectorAll<HTMLInputElement>('input[name="signer_mode"]');
    const otherFields = document.getElementById('otherSignerFields');
    const syncMode = () => {
        const selected = document.querySelector<HTMLInputElement>('input[name="signer_mode"]:checked');
        if (!otherFields || !selected) {
            return;
        }
        const isOther = selected.value === 'other';
        otherFields.style.display = isOther ? 'block' : 'none';
        const email = document.getElementById('email') as HTMLInputElement | null;
        if (email) {
            email.required = isOther;
        }
    };
    modeRadios.forEach((radio) => radio.addEventListener('change', syncMode));
    syncMode();

    const prepare = document.getElementById('signPrepare');
    const fieldTools = document.getElementById('signFieldTools');
    const placeForm = document.getElementById('signPlaceForm') as HTMLFormElement | null;
    const placeType = document.getElementById('placeFieldType') as HTMLInputElement | null;
    const placeSignerId = document.getElementById('placeSignerId') as HTMLInputElement | null;
    const placeActiveSignerId = document.getElementById('placeActiveSignerId') as HTMLInputElement | null;
    const placePage = document.getElementById('placePage') as HTMLInputElement | null;
    const placePosX = document.getElementById('placePosX') as HTMLInputElement | null;
    const placePosY = document.getElementById('placePosY') as HTMLInputElement | null;
    const moveForm = document.getElementById('signMoveForm') as HTMLFormElement | null;
    const moveFieldId = document.getElementById('moveFieldId') as HTMLInputElement | null;
    const movePage = document.getElementById('movePage') as HTMLInputElement | null;
    const movePosX = document.getElementById('movePosX') as HTMLInputElement | null;
    const movePosY = document.getElementById('movePosY') as HTMLInputElement | null;
    const removeFieldForm = document.getElementById('signRemoveFieldForm') as HTMLFormElement | null;
    const removeFieldId = document.getElementById('removeFieldId') as HTMLInputElement | null;
    const activeFor = document.getElementById('signActiveFor');
    const placeHint = document.getElementById('signPlaceHint');
    const pdfFrame = document.getElementById('signPdfFrame');
    const pdfPages = document.getElementById('signPdfPages');
    const pdfLoading = document.getElementById('signPdfLoading');
    let selectedFieldType: string | null = null;
    let suppressPlaceClick = false;

    const clearPlacementMode = () => {
        selectedFieldType = null;
        fieldTools?.querySelectorAll('.sign-tool-btn').forEach((item) => item.classList.remove('is-active'));
        pdfFrame?.classList.remove('is-placing');
        if (placeHint) {
            placeHint.hidden = true;
            placeHint.textContent = '';
        }
    };

    const setActiveSigner = (chip: HTMLElement) => {
        const signerId = chip.dataset.signerId || '';
        const signerName = chip.dataset.signerName || '';
        const signerColor = chip.dataset.signerColor || '#003FA5';

        document.querySelectorAll<HTMLElement>('.sign-chip[data-signer-id]').forEach((item) => {
            item.classList.toggle('is-active', item === chip);
        });

        if (placeSignerId) placeSignerId.value = signerId;
        if (placeActiveSignerId) placeActiveSignerId.value = signerId;
        document.querySelectorAll<HTMLInputElement>('input[name="active_signer_id"]').forEach((input) => {
            input.value = signerId;
        });

        if (activeFor) {
            activeFor.hidden = !signerId;
            activeFor.style.setProperty('--signer-color', signerColor);
            const template = activeFor.dataset.template || 'Felder für: %name%';
            activeFor.textContent = template.replace('%name%', signerName);
        }

        fieldTools?.classList.toggle('is-disabled', !signerId);
        pdfFrame?.classList.toggle('is-place-ready', Boolean(signerId));
        if (!signerId) {
            clearPlacementMode();
        }

        const url = new URL(window.location.href);
        if (signerId) {
            url.searchParams.set('signer', signerId);
        } else {
            url.searchParams.delete('signer');
        }
        window.history.replaceState({}, '', url.toString());
    };

    document.querySelectorAll<HTMLElement>('.sign-chip[data-signer-id]').forEach((chip) => {
        chip.addEventListener('click', () => setActiveSigner(chip));
    });

    fieldTools?.querySelectorAll<HTMLButtonElement>('.sign-tool-btn').forEach((btn) => {
        btn.addEventListener('click', () => {
            if (fieldTools.classList.contains('is-disabled') || !placeSignerId?.value) {
                alert(prepare?.dataset.needSigner || 'Bitte zuerst einen Unterzeichner auswählen.');
                return;
            }
            const nextType = btn.dataset.fieldType || 'signature';
            if (selectedFieldType === nextType) {
                clearPlacementMode();
                return;
            }
            selectedFieldType = nextType;
            fieldTools.querySelectorAll('.sign-tool-btn').forEach((item) => {
                item.classList.toggle('is-active', item === btn);
            });
            if (placeType) {
                placeType.value = selectedFieldType;
            }
            if (placeHint) {
                placeHint.hidden = false;
                placeHint.textContent = prepare?.dataset.placeHint || '';
            }
            pdfFrame?.classList.add('is-placing');
        });
    });

    document.addEventListener('keydown', (event) => {
        if (event.key === 'Escape' && selectedFieldType) {
            clearPlacementMode();
        }
    });

    const placeAtPage = async (pageEl: HTMLElement, clientX: number, clientY: number) => {
        if (!placeForm || !selectedFieldType || !placeSignerId?.value) {
            return;
        }
        const rect = pageEl.getBoundingClientRect();
        const x = ((clientX - rect.left) / rect.width) * 100;
        const y = ((clientY - rect.top) / rect.height) * 100;
        const page = Number(pageEl.dataset.page || '1');
        if (placePage) placePage.value = String(page);
        if (placePosX) placePosX.value = String(Math.max(1, Math.min(90, x)));
        if (placePosY) placePosY.value = String(Math.max(1, Math.min(90, y)));
        if (placeType) placeType.value = selectedFieldType;

        const workspace = document.querySelector('.sign-prepare__workspace') as HTMLElement | null;
        const scrollTop = workspace?.scrollTop ?? 0;

        try {
            const response = await fetch(window.location.pathname + window.location.search, {
                method: 'POST',
                body: new FormData(placeForm),
                credentials: 'same-origin',
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    Accept: 'application/json',
                },
            });
            const raw = await response.text();
            let data: {
                ok?: boolean;
                error?: string;
                field?: SignFieldData;
                fieldsCount?: number;
            };
            try {
                data = JSON.parse(raw);
            } catch (parseError) {
                throw new Error('Ungültige Server-Antwort beim Platzieren.');
            }
            if (!response.ok || !data.ok || !data.field) {
                alert(data.error || prepare?.dataset.needSigner || 'Feld konnte nicht platziert werden.');
                return;
            }

            const layer = pageEl.querySelector('.sign-pdf-page__layer');
            if (layer) {
                const box = createFieldBox(data.field);
                box.classList.add('is-just-placed');
                layer.appendChild(box);
                window.setTimeout(() => box.classList.remove('is-just-placed'), 700);
            }

            if (typeof data.fieldsCount === 'number') {
                updateFieldsCount(data.fieldsCount);
            }

            if (workspace) {
                workspace.scrollTop = scrollTop;
            }
        } catch (error) {
            console.error(error);
            alert('Feld konnte nicht platziert werden. Bitte Seite neu laden und erneut versuchen.');
        }
    };

    const updateFieldsCount = (count: number) => {
        const countEl = document.getElementById('signFieldsCount');
        if (countEl) {
            countEl.textContent = String(count);
        }
    };

    const saveFieldPosition = async (fieldEl: HTMLElement) => {
        if (!moveForm || !moveFieldId || !movePage || !movePosX || !movePosY) {
            return;
        }
        const pageEl = fieldEl.closest('.sign-pdf-page') as HTMLElement | null;
        if (!pageEl) {
            return;
        }
        moveFieldId.value = fieldEl.dataset.fieldId || '';
        movePage.value = pageEl.dataset.page || '1';
        movePosX.value = String(parseFloat(fieldEl.style.left) || 0);
        movePosY.value = String(parseFloat(fieldEl.style.top) || 0);

        const formData = new FormData(moveForm);
        try {
            await fetch(window.location.pathname + window.location.search, {
                method: 'POST',
                body: formData,
                credentials: 'same-origin',
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    Accept: 'application/json',
                },
            });
        } catch (error) {
            console.error(error);
        }
    };

    const bindFieldInteractions = (fieldEl: HTMLElement) => {
        const removeBtn = fieldEl.querySelector('.sign-field-box__remove');
        removeBtn?.addEventListener('click', async (event) => {
            event.preventDefault();
            event.stopPropagation();
            if (!removeFieldForm || !removeFieldId) {
                return;
            }
            removeFieldId.value = fieldEl.dataset.fieldId || '';
            try {
                const response = await fetch(window.location.pathname + window.location.search, {
                    method: 'POST',
                    body: new FormData(removeFieldForm),
                    credentials: 'same-origin',
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest',
                        Accept: 'application/json',
                    },
                });
                const data = await response.json() as { ok?: boolean; fieldsCount?: number };
                if (response.ok && data.ok) {
                    fieldEl.remove();
                    if (typeof data.fieldsCount === 'number') {
                        updateFieldsCount(data.fieldsCount);
                    }
                    return;
                }
            } catch (error) {
                console.error(error);
            }
            alert('Feld konnte nicht entfernt werden.');
        });

        let dragging = false;
        let startX = 0;
        let startY = 0;
        let originLeft = 0;
        let originTop = 0;

        fieldEl.addEventListener('pointerdown', (event) => {
            if ((event.target as HTMLElement).closest('.sign-field-box__remove')) {
                return;
            }
            const pageEl = fieldEl.closest('.sign-pdf-page') as HTMLElement | null;
            if (!pageEl) {
                return;
            }
            dragging = true;
            suppressPlaceClick = true;
            fieldEl.classList.add('is-dragging');
            fieldEl.setPointerCapture(event.pointerId);
            startX = event.clientX;
            startY = event.clientY;
            originLeft = parseFloat(fieldEl.style.left) || 0;
            originTop = parseFloat(fieldEl.style.top) || 0;
            event.preventDefault();
        });

        fieldEl.addEventListener('pointermove', (event) => {
            if (!dragging) {
                return;
            }
            const pageEl = fieldEl.closest('.sign-pdf-page') as HTMLElement | null;
            if (!pageEl) {
                return;
            }
            const rect = pageEl.getBoundingClientRect();
            const deltaX = ((event.clientX - startX) / rect.width) * 100;
            const deltaY = ((event.clientY - startY) / rect.height) * 100;
            const nextLeft = Math.max(0, Math.min(90, originLeft + deltaX));
            const nextTop = Math.max(0, Math.min(92, originTop + deltaY));
            fieldEl.style.left = `${nextLeft}%`;
            fieldEl.style.top = `${nextTop}%`;
        });

        const endDrag = async (event: PointerEvent) => {
            if (!dragging) {
                return;
            }
            dragging = false;
            fieldEl.classList.remove('is-dragging');
            try {
                fieldEl.releasePointerCapture(event.pointerId);
            } catch (error) {
                // ignore
            }
            await saveFieldPosition(fieldEl);
            window.setTimeout(() => {
                suppressPlaceClick = false;
            }, 50);
        };

        fieldEl.addEventListener('pointerup', endDrag);
        fieldEl.addEventListener('pointercancel', endDrag);
    };

    const createFieldBox = (field: SignFieldData): HTMLElement => {
        const box = document.createElement('div');
        box.className = 'sign-field-box';
        box.dataset.fieldId = String(field.id);
        box.dataset.fieldType = field.type;
        box.style.left = `${field.posX}%`;
        box.style.top = `${field.posY}%`;
        box.style.width = `${field.width}%`;
        box.style.height = `${field.height}%`;
        box.style.setProperty('--signer-color', field.color);
        box.innerHTML = `
            <button type="button" class="sign-field-box__remove" aria-label="Remove">×</button>
            <span>${field.label}</span>
            ${field.signerName ? `<small>${field.signerName}</small>` : ''}
        `;
        bindFieldInteractions(box);
        return box;
    };

    const renderPdfViewer = async () => {
        if (!pdfFrame || !pdfPages || !window.pdfjsLib) {
            if (pdfLoading) {
                pdfLoading.textContent = 'PDF-Viewer konnte nicht geladen werden.';
            }
            return;
        }

        const pdfUrl = pdfFrame.dataset.pdfUrl;
        if (!pdfUrl) {
            return;
        }

        let fields: SignFieldData[] = [];
        const fieldsNode = document.getElementById('signFieldsData');
        if (fieldsNode?.textContent) {
            try {
                fields = JSON.parse(fieldsNode.textContent) as SignFieldData[];
            } catch (error) {
                fields = [];
            }
        }

        try {
            const pdf = await window.pdfjsLib.getDocument({
                url: pdfUrl,
                withCredentials: true,
            }).promise;

            pdfPages.innerHTML = '';
            const baseWidth = Math.min(820, pdfFrame.clientWidth || 820);

            for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber += 1) {
                const page = await pdf.getPage(pageNumber);
                const unscaled = page.getViewport({ scale: 1 });
                const scale = baseWidth / unscaled.width;
                const viewport = page.getViewport({ scale });

                const pageEl = document.createElement('div');
                pageEl.className = 'sign-pdf-page';
                pageEl.dataset.page = String(pageNumber);

                const canvas = document.createElement('canvas');
                canvas.width = viewport.width;
                canvas.height = viewport.height;
                canvas.className = 'sign-pdf-page__canvas';
                pageEl.appendChild(canvas);

                const layer = document.createElement('div');
                layer.className = 'sign-pdf-page__layer';
                pageEl.appendChild(layer);

                pdfPages.appendChild(pageEl);

                const context = canvas.getContext('2d');
                if (context) {
                    await page.render({ canvasContext: context, viewport }).promise;
                }

                pageEl.addEventListener('click', (event) => {
                    if (!selectedFieldType || suppressPlaceClick) {
                        return;
                    }
                    if ((event.target as HTMLElement).closest('.sign-field-box')) {
                        return;
                    }
                    placeAtPage(pageEl, event.clientX, event.clientY);
                });

                fields
                    .filter((field) => field.page === pageNumber)
                    .forEach((field) => layer.appendChild(createFieldBox(field)));
            }

            pdfPages.hidden = false;
            if (pdfLoading) {
                pdfLoading.hidden = true;
            }
        } catch (error) {
            console.error(error);
            if (pdfLoading) {
                pdfLoading.textContent = 'PDF konnte nicht geladen werden.';
            }
        }
    };

    if (pdfFrame) {
        void renderPdfViewer();
    }

    const showApp = document.getElementById('signShowApp');
    const showPages = document.getElementById('signShowPages');
    const showLoading = document.getElementById('signShowLoading');
    const showFrame = document.getElementById('signShowPdfFrame');

    const createFilledFieldBox = (field: SignFieldData): HTMLElement => {
        const box = document.createElement('div');
        box.className = 'sign-field-box sign-field-box--view';
        if (field.completed || field.value) {
            box.classList.add('is-filled');
        }
        box.style.left = `${field.posX}%`;
        box.style.top = `${field.posY}%`;
        box.style.width = `${field.width}%`;
        box.style.height = `${field.height}%`;
        box.style.setProperty('--signer-color', field.color);

        const value = field.value || '';
        if (field.type === 'signature' && value.startsWith('data:image')) {
            box.innerHTML = `<img src="${value}" alt="${field.label}">`;
        } else if (value) {
            box.innerHTML = `<span>${value}</span><small>${field.signerName || ''}</small>`;
        } else {
            box.innerHTML = `<span>${field.label}</span><small>${field.signerName || ''}</small>`;
        }
        return box;
    };

    const renderShowViewer = async () => {
        if (!showApp || !showPages || !window.pdfjsLib) {
            if (showLoading) {
                showLoading.textContent = 'PDF-Viewer konnte nicht geladen werden.';
            }
            return;
        }

        const pdfUrl = showApp.dataset.pdfUrl;
        if (!pdfUrl) {
            return;
        }

        let fields: SignFieldData[] = [];
        const fieldsNode = document.getElementById('signShowFields');
        if (fieldsNode?.textContent) {
            try {
                fields = JSON.parse(fieldsNode.textContent) as SignFieldData[];
            } catch (error) {
                fields = [];
            }
        }

        try {
            const pdf = await window.pdfjsLib.getDocument({
                url: pdfUrl,
                withCredentials: true,
            }).promise;

            showPages.innerHTML = '';
            const baseWidth = Math.min(820, showFrame?.clientWidth || showApp.clientWidth || 820);

            for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber += 1) {
                const page = await pdf.getPage(pageNumber);
                const unscaled = page.getViewport({ scale: 1 });
                const scale = baseWidth / unscaled.width;
                const viewport = page.getViewport({ scale });

                const pageEl = document.createElement('div');
                pageEl.className = 'sign-pdf-page';
                pageEl.dataset.page = String(pageNumber);

                const canvas = document.createElement('canvas');
                canvas.width = viewport.width;
                canvas.height = viewport.height;
                canvas.className = 'sign-pdf-page__canvas';
                pageEl.appendChild(canvas);

                const layer = document.createElement('div');
                layer.className = 'sign-pdf-page__layer';
                pageEl.appendChild(layer);
                showPages.appendChild(pageEl);

                const context = canvas.getContext('2d');
                if (context) {
                    await page.render({ canvasContext: context, viewport }).promise;
                }

                fields
                    .filter((field) => field.page === pageNumber)
                    .forEach((field) => layer.appendChild(createFilledFieldBox(field)));
            }

            showPages.hidden = false;
            if (showLoading) {
                showLoading.hidden = true;
            }
        } catch (error) {
            console.error(error);
            if (showLoading) {
                showLoading.textContent = 'PDF konnte nicht geladen werden.';
            }
        }
    };

    if (showApp) {
        void renderShowViewer();
    }
});
