import { addDays, startOfMonth, startOfWeekMonday } from '../services/dateUtils.js';

const DEFAULT_WEEKDAYS = ['pn', 'wt', 'śr', 'cz', 'pt', 'sb', 'nd'];
const DEFAULT_MONTHS_LONG = [
    'Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec',
    'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień',
];

const DEFAULT_THEME = {
    selectedBg: 'rgb(var(--card-rgb))',
    selectedBorder: 'rgb(var(--border-input-rgb))',
    selectedText: 'rgb(var(--text-emphasis-rgb))',
    selectedDot: 'rgb(var(--text-emphasis-rgb))',
    bg: 'rgb(var(--app-bg-rgb))',
    border: 'rgb(var(--card-raised-rgb))',
    text: 'rgb(var(--text-body-soft-rgb))',
    dimmedBg: 'transparent',
    dimText: 'rgb(var(--text-faint-rgb))',
    dimOpacity: 0.58,
    dot: 'rgb(var(--text-faint-rgb))',
};

function dateKeyLocal(date) {
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, '0');
    const d = String(date.getDate()).padStart(2, '0');
    return `${y}-${m}-${d}`;
}

function keyToDate(key) {
    return new Date(`${key}T00:00:00`);
}

function normalizeMonth(date) {
    return startOfMonth(date instanceof Date ? date : new Date());
}

function monthLabel(date, monthsLong) {
    return `${monthsLong[date.getMonth()]} ${date.getFullYear()}`;
}

function dayRange(aKey, bKey) {
    const a = keyToDate(aKey);
    const b = keyToDate(bKey);
    const [from, to] = a <= b ? [a, b] : [b, a];
    const out = [];
    for (let d = new Date(from); d <= to; d = addDays(d, 1)) out.push(dateKeyLocal(d));
    return out;
}

export function createSwipePopoverCalendarHTML({
    idPrefix,
    weekdays = DEFAULT_WEEKDAYS,
    monthLabelTextClass = 'text-[10px] font-semibold leading-none tabular-nums whitespace-nowrap',
}) {
    const weekdayHeader = `
        <div class="grid grid-cols-7 gap-1.5 text-center text-[8px] font-medium uppercase tracking-wide mb-1 leading-none" style="color:rgb(var(--text-dim-rgb));">
            ${weekdays.map((d) => `<div>${d}</div>`).join('')}
        </div>
    `;
    const gripLeft = `
        <div data-swc-grip aria-hidden="true" style="position:absolute; left:0; top:50%; transform:translateY(-50%); width:0.76rem; height:1rem; pointer-events:none; opacity:0.66;">
            <span style="position:absolute; left:0; top:50%; transform:translateY(-50%); width:1.5px; height:1.22rem; border-radius:999px; background:rgba(var(--text-faint-rgb),0.84);"></span>
            <span style="position:absolute; left:0.16rem; top:50%; transform:translateY(-50%); width:1.5px; height:1.0rem; border-radius:999px; background:rgba(var(--text-faint-rgb),0.74);"></span>
            <span style="position:absolute; left:0.34rem; top:50%; transform:translateY(-50%); width:1.5px; height:0.56rem; border-radius:999px; background:rgba(var(--text-faint-rgb),0.62);"></span>
        </div>
    `;
    const gripRight = `
        <div data-swc-grip aria-hidden="true" style="position:absolute; right:0; top:50%; transform:translateY(-50%); width:0.76rem; height:1rem; pointer-events:none; opacity:0.66;">
            <span style="position:absolute; right:0; top:50%; transform:translateY(-50%); width:1.5px; height:1.22rem; border-radius:999px; background:rgba(var(--text-faint-rgb),0.84);"></span>
            <span style="position:absolute; right:0.16rem; top:50%; transform:translateY(-50%); width:1.5px; height:1.0rem; border-radius:999px; background:rgba(var(--text-faint-rgb),0.74);"></span>
            <span style="position:absolute; right:0.34rem; top:50%; transform:translateY(-50%); width:1.5px; height:0.56rem; border-radius:999px; background:rgba(var(--text-faint-rgb),0.62);"></span>
        </div>
    `;

    return `
        <div class="pb-3 px-3 flex items-center justify-end gap-3">
            <div class="flex h-[2.05rem] min-w-0 max-w-[min(100%,20rem)] items-center justify-center rounded-full border" style="background:rgb(var(--app-bg-rgb)); border-color:rgb(var(--card-raised-rgb));">
                <span id="${idPrefix}-month-label" class="h-full shrink-0 inline-flex min-w-[5.75rem] max-w-[9rem] items-center justify-center px-3 ${monthLabelTextClass}" style="color:rgb(var(--text-body-soft-rgb));"></span>
            </div>
        </div>
        <div id="${idPrefix}-viewport" style="overflow:hidden; position:relative; touch-action:pan-y;">
            <div id="${idPrefix}-track" style="display:flex; align-items:flex-start; position:relative; will-change:transform;">
                <div class="swc-panel" data-panel="prev" style="flex-shrink:0; pointer-events:none; position:relative; overflow:hidden;">
                    ${weekdayHeader}
                    <div id="${idPrefix}-prev-grid" class="grid grid-cols-7 gap-1.5"></div>
                    ${gripLeft}
                    ${gripRight}
                </div>
                <div class="swc-panel" data-panel="current" style="flex-shrink:0; position:relative; overflow:hidden;">
                    ${weekdayHeader}
                    <div id="${idPrefix}-grid" class="grid grid-cols-7 gap-1.5"></div>
                    ${gripLeft}
                    ${gripRight}
                </div>
                <div class="swc-panel" data-panel="next" style="flex-shrink:0; pointer-events:none; position:relative; overflow:hidden;">
                    ${weekdayHeader}
                    <div id="${idPrefix}-next-grid" class="grid grid-cols-7 gap-1.5"></div>
                    ${gripLeft}
                    ${gripRight}
                </div>
            </div>
        </div>
    `;
}

export function initSwipePopoverCalendar({
    idPrefix,
    selectionMode = 'single',
    monthsLong = DEFAULT_MONTHS_LONG,
    theme = DEFAULT_THEME,
    getMonthAnchor,
    setMonthAnchor,
    getSelectionKeys,
    onSelectionCommit,
    resolveDayState,
    panelHandlePx = null,
    panelHandleRatio = 0.045,
    panelHandleMin = 10,
    panelHandleMax = 16,
}) {
    const monthLabelEl = document.getElementById(`${idPrefix}-month-label`);
    const gridEl = document.getElementById(`${idPrefix}-grid`);
    const prevGridEl = document.getElementById(`${idPrefix}-prev-grid`);
    const nextGridEl = document.getElementById(`${idPrefix}-next-grid`);
    const track = document.getElementById(`${idPrefix}-track`);
    const viewport = document.getElementById(`${idPrefix}-viewport`);
    if (!gridEl || !track || !viewport) {
        return {
            render: () => {},
            reapplyLayout: () => {},
            resetTrackPosition: () => {},
        };
    }

    const MOVE_THRESHOLD = 6;
    const SWIPE_THRESHOLD = 40;
    const ANIMATION_MS = 260;

    let viewportWidth = 0;
    let panelWidth = 0;
    let panelHandle = 0;
    let dragHandleWidth = 0;
    let restOffset = 0;
    let animatingNav = false;

    const panels = Array.from(track.querySelectorAll('.swc-panel'));

    const syncGripVisibility = (showAdjacent = false) => {
        panels.forEach((panel) => {
            const isCurrent = panel.dataset.panel === 'current';
            panel.querySelectorAll('[data-swc-grip]').forEach((grip) => {
                grip.style.opacity = (isCurrent || showAdjacent) ? '0.66' : '0';
            });
        });
    };

    const applyLayout = () => {
        const vw = viewport.clientWidth || viewport.getBoundingClientRect().width;
        if (!vw) return;
        viewportWidth = vw;
        const computedHandle = panelHandlePx == null
            ? Math.round(vw * panelHandleRatio)
            : panelHandlePx;
        panelHandle = Math.max(panelHandleMin, Math.min(panelHandleMax, computedHandle));
        dragHandleWidth = panelHandle;
        panelWidth = vw;
        restOffset = -panelWidth;
        panels.forEach((panel) => {
            panel.style.width = `${panelWidth}px`;
            panel.style.boxSizing = 'border-box';
            panel.style.paddingLeft = `${panelHandle}px`;
            panel.style.paddingRight = `${panelHandle}px`;
        });
        track.style.transition = 'none';
        track.style.transform = `translate3d(${restOffset}px, 0, 0)`;
        syncGripVisibility(false);
    };

    const resetTrackPosition = () => {
        track.style.transition = 'none';
        track.style.transform = `translate3d(${restOffset}px, 0, 0)`;
        syncGripVisibility(false);
    };

    const setDragTranslate = (dx, ms) => {
        track.style.transition = ms ? `transform ${ms}ms ease` : 'none';
        track.style.transform = `translate3d(${restOffset + dx}px, 0, 0)`;
    };

    const getSelectedSet = (previewSelection = null) => {
        const raw = previewSelection ?? getSelectionKeys();
        if (Array.isArray(raw)) return new Set(raw);
        if (typeof raw === 'string' && raw) return new Set([raw]);
        return new Set();
    };

    const renderMonthGrid = (targetGrid, monthAnchor, selectedSet) => {
        if (!targetGrid) return;
        const first = startOfMonth(monthAnchor);
        const gridStart = startOfWeekMonday(first);
        const cells = Array.from({ length: 42 }, (_, i) => addDays(gridStart, i));
        targetGrid.innerHTML = cells.map((day) => {
            const dk = dateKeyLocal(day);
            const inCurrentMonth = day.getMonth() === first.getMonth();
            const isSelected = selectedSet.has(dk);
            const resolved = (typeof resolveDayState === 'function'
                ? resolveDayState(day, { inCurrentMonth, isSelected })
                : {}) || {};
            const disabled = !!resolved.disabled;
            const dimmed = !!resolved.dimmed;
            const showDot = !!resolved.showDot;

            let bg;
            let borderColor;
            let text;
            let borderClass = 'border';
            if (isSelected) {
                bg = theme.selectedBg;
                borderColor = theme.selectedBorder;
                text = theme.selectedText;
            } else if (dimmed) {
                bg = theme.dimmedBg;
                borderColor = 'transparent';
                text = theme.dimText;
                borderClass = 'border-0';
            } else {
                bg = theme.bg;
                borderColor = theme.border;
                text = theme.text;
            }
            const opacity = dimmed && !isSelected ? String(theme.dimOpacity ?? 0.58) : '1';
            const dotColor = isSelected ? theme.selectedDot : theme.dot;
            const tag = disabled ? 'div' : 'button';
            const attrs = disabled ? '' : `type="button" data-dk="${dk}"`;
            const dayClass = disabled ? '' : ' swc-day';

            return `
                <${tag} ${attrs}
                    class="mx-auto flex h-[2.05rem] w-full min-w-0 max-w-full items-center justify-center rounded-full ${borderClass}${dayClass} text-xs font-medium leading-tight overflow-hidden"
                    style="background:${bg}; border-color:${borderColor}; color:${text}; opacity:${opacity}; touch-action:none;">
                    <span class="relative flex h-full w-full flex-col items-center justify-center">
                        <span class="text-[13px] font-semibold leading-none ${showDot ? '-translate-y-[0.18rem]' : ''}">${day.getDate()}</span>
                        ${showDot ? `<span class="absolute left-1/2 w-1 h-1 -translate-x-1/2 rounded-full opacity-75" style="bottom:0.24rem; background:${dotColor};" aria-hidden="true"></span>` : ''}
                    </span>
                </${tag}>
            `;
        }).join('');
    };

    const render = (previewSelection = null) => {
        const anchor = normalizeMonth(getMonthAnchor());
        const selectedSet = getSelectedSet(previewSelection);
        if (monthLabelEl) monthLabelEl.textContent = monthLabel(anchor, monthsLong);
        renderMonthGrid(gridEl, anchor, selectedSet);
        renderMonthGrid(prevGridEl, new Date(anchor.getFullYear(), anchor.getMonth() - 1, 1), selectedSet);
        renderMonthGrid(nextGridEl, new Date(anchor.getFullYear(), anchor.getMonth() + 1, 1), selectedSet);
        applyLayout();
    };

    const commitNavigation = (monthDelta) => {
        animatingNav = true;
        const targetDx = monthDelta < 0 ? panelWidth : -panelWidth;
        setDragTranslate(targetDx, ANIMATION_MS);
        setTimeout(() => {
            const anchor = normalizeMonth(getMonthAnchor());
            setMonthAnchor(startOfMonth(new Date(anchor.getFullYear(), anchor.getMonth() + monthDelta, 1)));
            render();
            resetTrackPosition();
            animatingNav = false;
        }, ANIMATION_MS);
    };

    if (selectionMode === 'range') {
        let dragStart = null;
        let dragCurrent = null;
        let dragging = false;
        gridEl.addEventListener('pointerdown', (e) => {
            if (animatingNav) return;
            const btn = e.target.closest('.swc-day');
            if (!btn) return;
            e.stopPropagation();
            dragStart = btn.dataset.dk;
            dragCurrent = btn.dataset.dk;
            dragging = true;
            gridEl.setPointerCapture(e.pointerId);
            render([dragStart]);
        });
        gridEl.addEventListener('pointermove', (e) => {
            if (!dragging || animatingNav) return;
            e.preventDefault();
            const el = document.elementFromPoint(e.clientX, e.clientY);
            const btn = el?.closest('.swc-day');
            if (btn?.dataset.dk && btn.dataset.dk !== dragCurrent) {
                dragCurrent = btn.dataset.dk;
                render(dayRange(dragStart, dragCurrent));
            }
        });
        gridEl.addEventListener('pointerup', () => {
            if (!dragging) return;
            dragging = false;
            const range = dayRange(dragStart, dragCurrent);
            dragStart = null;
            dragCurrent = null;
            if (typeof onSelectionCommit === 'function') onSelectionCommit(range);
            render();
        });
        gridEl.addEventListener('pointercancel', () => {
            dragging = false;
            dragStart = null;
            dragCurrent = null;
            render();
        });
    } else {
        gridEl.addEventListener('click', (e) => {
            const btn = e.target.closest('.swc-day');
            if (!btn) return;
            if (typeof onSelectionCommit === 'function') onSelectionCommit(btn.dataset.dk);
            render();
        });
    }

    let ptrId = null;
    let startX = 0;
    let startY = 0;
    let moved = false;
    let axis = null;

    viewport.addEventListener('pointerdown', (e) => {
        if (animatingNav || ptrId !== null) return;
        if (e.pointerType === 'mouse' && e.button !== 0) return;
        if (!panelWidth) applyLayout();
        const rect = viewport.getBoundingClientRect();
        const localX = e.clientX - rect.left;
        const inLeft = localX <= dragHandleWidth;
        const inRight = localX >= (viewportWidth - dragHandleWidth);
        if (!inLeft && !inRight) return;
        ptrId = e.pointerId;
        startX = e.clientX;
        startY = e.clientY;
        moved = false;
        axis = null;
        try { viewport.setPointerCapture(e.pointerId); } catch (_) {}
    });

    viewport.addEventListener('pointermove', (e) => {
        if (e.pointerId !== ptrId || animatingNav) return;
        const dx = e.clientX - startX;
        const dy = e.clientY - startY;
        if (!moved && (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)) {
            moved = true;
            axis = Math.abs(dx) >= Math.abs(dy) ? 'x' : 'y';
            if (axis === 'x') syncGripVisibility(true);
        }
        if (axis === 'x') setDragTranslate(dx, 0);
    });

    const endGesture = (e) => {
        if (e && e.pointerId !== ptrId) return;
        ptrId = null;
        if (!moved || axis !== 'x') return;
        const dx = e ? e.clientX - startX : 0;
        if (Math.abs(dx) >= SWIPE_THRESHOLD) commitNavigation(dx > 0 ? -1 : 1);
        else {
            setDragTranslate(0, ANIMATION_MS);
            setTimeout(() => syncGripVisibility(false), ANIMATION_MS + 20);
        }
        moved = false;
        axis = null;
    };

    viewport.addEventListener('pointerup', endGesture);
    viewport.addEventListener('pointercancel', endGesture);
    window.addEventListener('resize', applyLayout);

    return { render, reapplyLayout: applyLayout, resetTrackPosition };
}
