import './styles/collect.scss';
import { Modal } from 'bootstrap';
import { createCollectFeedback } from './collect_feedback';

// Bootstrap appends .modal-backdrop to <body>. Modals that stay inside
// `.z-index-10` end up under that backdrop → visible but not clickable.
document.querySelectorAll<HTMLElement>('.collect-modal').forEach((modal) => {
    if (modal.parentElement !== document.body) {
        document.body.appendChild(modal);
    }
});

// Open from ⋮ after dropdown starts closing — avoids focus fights with Dropdown.
document.querySelectorAll<HTMLButtonElement>('[data-collect-modal]').forEach((btn) => {
    btn.addEventListener('click', () => {
        const target = btn.dataset.collectModal;
        if (!target) return;
        const el = document.querySelector(target);
        if (!el) return;
        window.setTimeout(() => {
            Modal.getOrCreateInstance(el).show();
        }, 50);
    });
});

const album = document.getElementById('collectAlbumApp');
const fileInput = document.getElementById('collectFileInput') as HTMLInputElement | null;
const addBtn = document.getElementById('collectAddBtn');
const hero = document.getElementById('collectHero');

type AlbumItem = {
    kind: 'image' | 'video' | 'file';
    name: string;
    preview: string;
    download: string;
    fileId?: string;
    el: HTMLButtonElement;
};

const albumItems: AlbumItem[] = Array.from(
    document.querySelectorAll<HTMLButtonElement>('.collect-thumb')
).map((el) => ({
    kind: (el.dataset.kind as AlbumItem['kind']) || 'file',
    name: el.dataset.name || '',
    preview: el.dataset.preview || '',
    download: el.dataset.download || '',
    fileId: el.dataset.fileId || undefined,
    el,
}));

const mediaItems = albumItems.filter((i) => (i.kind === 'image' || i.kind === 'video') && i.preview);

let activeHeroItem: AlbumItem | null = albumItems.find((item) => item.el.classList.contains('is-active')) || albumItems[0] || null;

const heroFeedback = album
    ? createCollectFeedback({
          suffix: 'Hero',
          urls: {
              feedback: album.dataset.feedbackUrl || '',
              rate: album.dataset.feedbackRateUrl || '',
              comment: album.dataset.feedbackCommentUrl || '',
              viewed: album.dataset.feedbackViewedUrl || '',
          },
          csrfToken: album.dataset.feedbackCsrf,
          labels: {
              rate: album.dataset.feedbackRateLabel || 'Rate this image',
              average: album.dataset.feedbackAverageLabel || '%average% / 5 (%count%)',
              commentPlaceholder: album.dataset.feedbackCommentPlaceholder || '',
              commentSubmit: album.dataset.feedbackCommentSubmit || 'Send',
              noComments: album.dataset.feedbackNoComments || 'No comments yet',
              missingActor: album.dataset.feedbackMissingActor || 'Email required',
          },
          getCurrentItem: () => {
              if (!activeHeroItem || activeHeroItem.kind !== 'image') return null;
              return { kind: activeHeroItem.kind, fileId: activeHeroItem.fileId };
          },
      })
    : { refresh: () => undefined };

const syncActiveImage = (item: AlbumItem | null) => {
    if (!item) return;
    activeHeroItem = item;
    document.querySelectorAll('.collect-thumb').forEach((t) => t.classList.remove('is-active'));
    item.el.classList.add('is-active');
    if (item.kind === 'image') {
        heroFeedback.refresh();
    } else {
        document.getElementById('collectHeroFeedback')?.setAttribute('hidden', 'hidden');
    }
};

const lightbox = document.getElementById('collectLightbox');
const mediaHost = document.getElementById('collectLightboxMedia');
const nameEl = document.getElementById('collectLightboxName');
const counterEl = document.getElementById('collectLightboxCounter');
const downloadBtn = document.getElementById('collectLightboxDownload') as HTMLAnchorElement | null;

let mediaIndex = 0;
let zoom = 1;
let panX = 0;
let panY = 0;
let dragging = false;
let dragStartX = 0;
let dragStartY = 0;
let panStartX = 0;
let panStartY = 0;

const escAttr = (value: string) =>
    value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');

const renderHero = (item: AlbumItem) => {
    if (!hero) return;
    if (item.kind === 'image' && item.preview) {
        hero.innerHTML = `<img class="collect-album__hero-media is-clickable" src="${escAttr(item.preview)}" alt="${escAttr(item.name)}" data-lightbox-open>`;
        syncActiveImage(item);
        return;
    }
    if (item.kind === 'video' && item.preview) {
        hero.innerHTML = `<video class="collect-album__hero-media" controls playsinline preload="metadata" src="${escAttr(item.preview)}"></video>`;
        syncActiveImage(item);
        return;
    }
    hero.innerHTML = `
        <div class="collect-album__hero-file">
            <strong>${escAttr(item.name)}</strong>
            <a class="collect-btn" href="${escAttr(item.download || '#')}">Download</a>
        </div>
    `;
    syncActiveImage(item);
};

const applyZoom = () => {
    const target = mediaHost?.querySelector<HTMLElement>('img, video');
    if (!target) return;
    target.style.transform = `translate(${panX}px, ${panY}px) scale(${zoom})`;
    target.classList.toggle('is-zoomed', zoom > 1);
};

const resetZoom = () => {
    zoom = 1;
    panX = 0;
    panY = 0;
    applyZoom();
};

const renderLightboxSlide = () => {
    if (!mediaHost || !mediaItems.length) return;
    const item = mediaItems[mediaIndex];
    mediaHost.innerHTML = '';
    resetZoom();

    if (item.kind === 'video') {
        const video = document.createElement('video');
        video.src = item.preview;
        video.controls = true;
        video.playsInline = true;
        video.autoplay = true;
        video.className = 'collect-lightbox__video';
        mediaHost.appendChild(video);
    } else {
        const img = document.createElement('img');
        img.src = item.preview;
        img.alt = item.name;
        img.className = 'collect-lightbox__image';
        img.draggable = false;
        mediaHost.appendChild(img);
    }

    if (nameEl) nameEl.textContent = item.name;
    if (counterEl) counterEl.textContent = `${mediaIndex + 1} / ${mediaItems.length}`;
    if (downloadBtn) {
        downloadBtn.href = item.download || '#';
        downloadBtn.hidden = !item.download;
    }

    syncActiveImage(item);
};

const openLightbox = (index: number) => {
    if (!lightbox || !mediaItems.length) return;
    mediaIndex = Math.max(0, Math.min(index, mediaItems.length - 1));
    lightbox.hidden = false;
    document.body.classList.add('collect-lightbox-open');
    renderLightboxSlide();
};

const closeLightbox = () => {
    if (!lightbox) return;
    mediaHost?.querySelector('video')?.pause();
    lightbox.hidden = true;
    document.body.classList.remove('collect-lightbox-open');
    if (mediaHost) mediaHost.innerHTML = '';
};

const stepLightbox = (delta: number) => {
    if (!mediaItems.length) return;
    mediaIndex = (mediaIndex + delta + mediaItems.length) % mediaItems.length;
    renderLightboxSlide();
};

albumItems.forEach((item) => {
    item.el.addEventListener('click', () => {
        document.querySelectorAll('.collect-thumb').forEach((t) => t.classList.remove('is-active'));
        item.el.classList.add('is-active');
        renderHero(item);
    });

    item.el.addEventListener('dblclick', () => {
        if ((item.kind === 'image' || item.kind === 'video') && item.preview) {
            const idx = mediaItems.indexOf(item);
            openLightbox(idx >= 0 ? idx : 0);
        }
    });
});

hero?.addEventListener('click', (e) => {
    const target = e.target as HTMLElement;
    if (target.matches('[data-lightbox-open], img.is-clickable')) {
        const active = document.querySelector<HTMLButtonElement>('.collect-thumb.is-active');
        const item = albumItems.find((i) => i.el === active) || mediaItems[0];
        if (!item) return;
        const idx = mediaItems.indexOf(item);
        if (idx >= 0) openLightbox(idx);
    }
});

if (activeHeroItem) {
    syncActiveImage(activeHeroItem);
}

lightbox?.querySelectorAll('[data-lightbox-close]').forEach((el) => {
    el.addEventListener('click', closeLightbox);
});
lightbox?.querySelector('[data-lightbox-prev]')?.addEventListener('click', () => stepLightbox(-1));
lightbox?.querySelector('[data-lightbox-next]')?.addEventListener('click', () => stepLightbox(1));

document.getElementById('collectLightboxZoomIn')?.addEventListener('click', () => {
    zoom = Math.min(4, zoom + 0.25);
    applyZoom();
});
document.getElementById('collectLightboxZoomOut')?.addEventListener('click', () => {
    zoom = Math.max(1, zoom - 0.25);
    if (zoom === 1) {
        panX = 0;
        panY = 0;
    }
    applyZoom();
});
document.getElementById('collectLightboxZoomReset')?.addEventListener('click', resetZoom);

mediaHost?.addEventListener('wheel', (e) => {
    const item = mediaItems[mediaIndex];
    if (!item || item.kind !== 'image') return;
    e.preventDefault();
    zoom = Math.min(4, Math.max(1, e.deltaY < 0 ? zoom + 0.15 : zoom - 0.15));
    if (zoom === 1) {
        panX = 0;
        panY = 0;
    }
    applyZoom();
}, { passive: false });

mediaHost?.addEventListener('dblclick', () => {
    const item = mediaItems[mediaIndex];
    if (!item || item.kind !== 'image') return;
    if (zoom > 1) resetZoom();
    else {
        zoom = 2;
        applyZoom();
    }
});

mediaHost?.addEventListener('pointerdown', (e) => {
    const item = mediaItems[mediaIndex];
    if (!item || item.kind !== 'image' || zoom <= 1) return;
    if ((e.target as HTMLElement).tagName !== 'IMG') return;
    dragging = true;
    dragStartX = e.clientX;
    dragStartY = e.clientY;
    panStartX = panX;
    panStartY = panY;
    mediaHost.setPointerCapture(e.pointerId);
});
mediaHost?.addEventListener('pointermove', (e) => {
    if (!dragging) return;
    panX = panStartX + (e.clientX - dragStartX);
    panY = panStartY + (e.clientY - dragStartY);
    applyZoom();
});
mediaHost?.addEventListener('pointerup', () => { dragging = false; });
mediaHost?.addEventListener('pointercancel', () => { dragging = false; });

const uploadPanel = document.getElementById('collectUploadPanel');
const uploadList = document.getElementById('collectUploadList');
const uploadTitle = document.getElementById('collectUploadTitle');
const uploadSummary = document.getElementById('collectUploadSummary');

type UploadLabels = {
    uploading: string;
    waiting: string;
    done: string;
    failed: string;
    progress: string;
    complete: string;
};

const getUploadLabels = (): UploadLabels => ({
    uploading: album?.dataset.uploadUploading || 'Uploading…',
    waiting: album?.dataset.uploadWaiting || 'Waiting…',
    done: album?.dataset.uploadDone || 'Done',
    failed: album?.dataset.uploadFailed || 'Upload failed',
    progress: album?.dataset.uploadProgress || 'Upload: %current% of %total%',
    complete: album?.dataset.uploadComplete || 'Upload complete',
});

const formatProgressLabel = (template: string, current: number, total: number): string =>
    template.replace('%current%', String(current)).replace('%total%', String(total));

let uploadBusy = false;

const setUploadItemState = (
    item: HTMLLIElement,
    state: 'waiting' | 'uploading' | 'done' | 'error',
    progress = 0,
    statusText = ''
) => {
    item.dataset.state = state;
    const statusEl = item.querySelector<HTMLElement>('.collect-upload__status');
    const fillEl = item.querySelector<HTMLElement>('.collect-upload__bar-fill');
    if (statusEl) statusEl.textContent = statusText;
    if (fillEl) fillEl.style.width = `${Math.max(0, Math.min(100, progress))}%`;
};

const createUploadItem = (file: File): HTMLLIElement => {
    const item = document.createElement('li');
    item.className = 'collect-upload__item';
    item.innerHTML = `
        <div class="collect-upload__item-head">
            <span class="collect-upload__name"></span>
            <span class="collect-upload__status"></span>
        </div>
        <div class="collect-upload__bar"><div class="collect-upload__bar-fill"></div></div>
    `;
    const nameEl = item.querySelector<HTMLElement>('.collect-upload__name');
    if (nameEl) nameEl.textContent = file.name;
    return item;
};

const uploadFileWithProgress = (
    url: string,
    file: File,
    onProgress: (percent: number) => void
): Promise<{ success: boolean; error?: string }> =>
    new Promise((resolve) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', url);
        xhr.withCredentials = true;
        xhr.upload.addEventListener('progress', (event) => {
            if (!event.lengthComputable) return;
            onProgress(Math.round((event.loaded / event.total) * 100));
        });
        xhr.addEventListener('load', () => {
            try {
                resolve(JSON.parse(xhr.responseText));
            } catch {
                resolve({ success: false, error: 'Upload failed' });
            }
        });
        xhr.addEventListener('error', () => resolve({ success: false, error: 'Upload failed' }));
        const body = new FormData();
        body.append('file', file);
        xhr.send(body);
    });

const uploadFiles = async (files: FileList | File[]) => {
    const url = album?.dataset.uploadUrl;
    if (!url || !files.length || uploadBusy) return;

    const labels = getUploadLabels();
    const fileArray = Array.from(files);
    uploadBusy = true;
    addBtn?.setAttribute('disabled', 'disabled');
    addBtn?.classList.add('is-busy');

    if (uploadPanel && uploadList) {
        uploadPanel.hidden = false;
        uploadList.innerHTML = '';
        if (uploadTitle) uploadTitle.textContent = labels.uploading;
        if (uploadSummary) uploadSummary.textContent = '';
    }

    const items = fileArray.map((file) => {
        const item = createUploadItem(file);
        uploadList?.appendChild(item);
        setUploadItemState(item, 'waiting', 0, labels.waiting);
        return item;
    });

    let hadError = false;
    for (let index = 0; index < fileArray.length; index += 1) {
        const file = fileArray[index];
        const item = items[index];
        if (uploadSummary) {
            uploadSummary.textContent = formatProgressLabel(labels.progress, index + 1, fileArray.length);
        }
        setUploadItemState(item, 'uploading', 0, labels.uploading);

        const result = await uploadFileWithProgress(url, file, (percent) => {
            setUploadItemState(item, 'uploading', percent, `${labels.uploading} ${percent}%`);
        });

        if (!result.success) {
            hadError = true;
            setUploadItemState(item, 'error', 100, result.error || labels.failed);
            window.alert(result.error || labels.failed);
            break;
        }

        setUploadItemState(item, 'done', 100, labels.done);
    }

    uploadBusy = false;
    addBtn?.removeAttribute('disabled');
    addBtn?.classList.remove('is-busy');

    if (!hadError) {
        if (uploadTitle) uploadTitle.textContent = labels.complete;
        if (uploadSummary) uploadSummary.textContent = '';
        window.setTimeout(() => window.location.reload(), 700);
    }
};

addBtn?.addEventListener('click', () => {
    if (!uploadBusy) fileInput?.click();
});
fileInput?.addEventListener('change', () => {
    if (fileInput.files?.length) void uploadFiles(fileInput.files);
    fileInput.value = '';
});

const shareModal = document.getElementById('collectShareModal');
const accessModal = document.getElementById('collectAccessModal');

const openOverlay = (el: HTMLElement | null) => {
    if (!el) return;
    if (el.parentElement !== document.body) {
        document.body.appendChild(el);
    }
    el.hidden = false;
    document.body.classList.add('collect-overlay-open');
};

const closeOverlays = () => {
    if (shareModal) shareModal.hidden = true;
    if (accessModal) accessModal.hidden = true;
    document.body.classList.remove('collect-overlay-open');
};

document.getElementById('collectShareOpen')?.addEventListener('click', () => openOverlay(shareModal));
document.getElementById('collectAccessOpen')?.addEventListener('click', () => openOverlay(accessModal));
document.getElementById('collectAccessOpenMenu')?.addEventListener('click', () => openOverlay(accessModal));
document.getElementById('collectOpenAccessFromShare')?.addEventListener('click', () => {
    if (shareModal) shareModal.hidden = true;
    openOverlay(accessModal);
});
document.getElementById('collectShowQrFromAccess')?.addEventListener('click', () => {
    if (accessModal) accessModal.hidden = true;
    openOverlay(shareModal);
});

document.querySelectorAll('[data-close-overlay]').forEach((el) => {
    el.addEventListener('click', closeOverlays);
});

if (shareModal?.dataset.openPanel === '1') openOverlay(shareModal);
if (accessModal?.dataset.openPanel === '1') openOverlay(accessModal);

document.querySelectorAll<HTMLButtonElement>('[data-copy]').forEach((btn) => {
    btn.addEventListener('click', async () => {
        const sel = btn.dataset.copy;
        const input = sel ? document.querySelector<HTMLInputElement>(sel) : null;
        if (!input) return;
        const original = btn.textContent;
        try {
            await navigator.clipboard.writeText(input.value);
            btn.textContent = 'OK';
        } catch {
            input.select();
            document.execCommand('copy');
            btn.textContent = 'OK';
        }
        window.setTimeout(() => {
            btn.textContent = original;
        }, 1200);
    });
});

document.querySelectorAll<HTMLButtonElement>('[data-copy-text]').forEach((btn) => {
    btn.addEventListener('click', async () => {
        const text = btn.dataset.copyText || '';
        if (!text) return;
        try {
            await navigator.clipboard.writeText(text);
            btn.textContent = 'OK';
        } catch {
            /* ignore */
        }
    });
});

document.addEventListener('keydown', (e) => {
    if (lightbox && !lightbox.hidden) {
        if (e.key === 'Escape') closeLightbox();
        if (e.key === 'ArrowLeft') stepLightbox(-1);
        if (e.key === 'ArrowRight') stepLightbox(1);
        return;
    }
    if (e.key === 'Escape') closeOverlays();
});

const drawQr = async (canvas: HTMLCanvasElement, text: string) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => {
        const ctx = canvas.getContext('2d');
        if (!ctx) return;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    };
    img.src = `https://api.qrserver.com/v1/create-qr-code/?size=${canvas.width}x${canvas.height}&data=${encodeURIComponent(text)}`;
};

document.querySelectorAll<HTMLCanvasElement>('canvas[data-qr]').forEach((canvas) => {
    const value = canvas.dataset.qr;
    if (value) void drawQr(canvas, value);
});

// Legacy wizard upload root (edit flow)
const uploadRoot = document.getElementById('collectUpload');
if (uploadRoot) {
    const input = document.getElementById('collectFileInput') as HTMLInputElement | null;
    const list = document.getElementById('collectUploadList');
    const url = uploadRoot.dataset.uploadUrl;

    const uploadFile = async (file: File) => {
        if (!url) return;
        const item = document.createElement('li');
        item.textContent = `${file.name} …`;
        list?.appendChild(item);

        const body = new FormData();
        body.append('file', file);
        try {
            const res = await fetch(url, { method: 'POST', body, credentials: 'same-origin' });
            const data = await res.json();
            item.textContent = data.success ? `${file.name} ✓` : `${file.name} ✗ (${data.error || res.status})`;
        } catch {
            item.textContent = `${file.name} ✗`;
        }
    };

    input?.addEventListener('change', () => {
        Array.from(input.files || []).forEach((file) => {
            void uploadFile(file);
        });
        input.value = '';
    });
}
