import { useEffect, useRef, useState } from 'react';
import { PlaceHolderImage } from '@/Utils/Other/PlaceholderImage';
import {
    CampaignAttachmentInput,
    parseCampaignAttachment,
    type BannerPosition,
} from '@/Utils/Other/campaignAttachment';

type CampaignBannerSlideProps = {
    item: CampaignAttachmentInput;
    userDetails: any;
    isFromCreate?: boolean;
    onCoverClick?: () => void;
    onBannerPositionChange?: (fileName: string, position: BannerPosition) => void;
};

type Metrics = { cw: number; ch: number; iw: number; ih: number };

type CoverLayout = {
    scaledW: number;
    scaledH: number;
    overflowX: number;
    overflowY: number;
    canDragX: boolean;
    canDragY: boolean;
};

type DragAxis = 'x' | 'y' | null;

const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));

/** Scale so one side fits the container edge and the other covers (no blank space). */
const getCoverLayout = (iw: number, ih: number, cw: number, ch: number): CoverLayout => {
    if (!iw || !ih || !cw || !ch) {
        return { scaledW: 0, scaledH: 0, overflowX: 0, overflowY: 0, canDragX: false, canDragY: false };
    }

    const scale = Math.max(cw / iw, ch / ih);
    const scaledW = iw * scale;
    const scaledH = ih * scale;
    const overflowX = Math.max(0, scaledW - cw);
    const overflowY = Math.max(0, scaledH - ch);

    return {
        scaledW,
        scaledH,
        overflowX,
        overflowY,
        canDragX: overflowX > 0.5,
        canDragY: overflowY > 0.5,
    };
};

const positionToTranslate = (position: number, overflow: number) =>
    overflow > 0 ? -((position / 100) * overflow) : 0;

const translateToPosition = (translate: number, overflow: number) =>
    overflow > 0 ? clamp((-translate / overflow) * 100, 0, 100) : 50;

const CampaignBannerSlide = ({
    item,
    userDetails,
    isFromCreate = false,
    onCoverClick,
    onBannerPositionChange,
}: CampaignBannerSlideProps) => {
    const { fileName, position } = parseCampaignAttachment(item);

    const containerRef = useRef<HTMLDivElement>(null);
    const imageRef = useRef<HTMLImageElement>(null);
    const metricsRef = useRef<Metrics | null>(null);
    const isDraggingRef = useRef(false);
    const lockedAxisRef = useRef<DragAxis>(null);
    const dragStartRef = useRef<{
        x: number;
        y: number;
        tx: number;
        ty: number;
        moved: boolean;
        pos: BannerPosition;
    } | null>(null);
    const listenersRef = useRef<{ move: (e: PointerEvent) => void; end: () => void } | null>(null);
    const onChangeRef = useRef(onBannerPositionChange);
    const onClickRef = useRef(onCoverClick);
    const fileNameRef = useRef(fileName);

    const [isDragging, setIsDragging] = useState(false);
    const [displayPosition, setDisplayPosition] = useState(position);
    const [metrics, setMetrics] = useState<Metrics | null>(null);
    const [imageSrc, setImageSrc] = useState('');

    const canReposition = isFromCreate && !!onBannerPositionChange && !!fileName;
    const activePosition = isDragging ? displayPosition : position;

    useEffect(() => {
        onChangeRef.current = onBannerPositionChange;
        onClickRef.current = onCoverClick;
        fileNameRef.current = fileName;
    }, [onBannerPositionChange, onCoverClick, fileName]);

    useEffect(() => {
        setImageSrc(fileName ? `${userDetails?.campaign_path || ''}${fileName}` : '');
        metricsRef.current = null;
        setMetrics(null);
    }, [fileName, userDetails?.campaign_path]);

    const recalc = () => {
        const container = containerRef.current;
        const image = imageRef.current;
        if (!container || !image?.naturalWidth || !image?.naturalHeight) return;

        const rect = container.getBoundingClientRect();
        const next: Metrics = {
            cw: rect.width,
            ch: rect.height,
            iw: image.naturalWidth,
            ih: image.naturalHeight,
        };
        metricsRef.current = next;
        setMetrics(next);
    };

    useEffect(() => {
        if (!isDraggingRef.current) setDisplayPosition(position);
    }, [position.x, position.y, fileName]);

    useEffect(() => {
        const container = containerRef.current;
        if (!container) return;
        const observer = new ResizeObserver(() => recalc());
        observer.observe(container);
        return () => observer.disconnect();
    }, [imageSrc]);

    useEffect(() => {
        const image = imageRef.current;
        if (image?.complete && image.naturalWidth > 0) recalc();
    }, [imageSrc]);

    const handleImageError = () => {
        setImageSrc(typeof PlaceHolderImage === 'string' ? PlaceHolderImage : PlaceHolderImage.src);
    };

    const endDrag = () => {
        if (!isDraggingRef.current) return;
        const moved = dragStartRef.current?.moved;
        isDraggingRef.current = false;
        lockedAxisRef.current = null;
        setIsDragging(false);
        dragStartRef.current = null;

        if (listenersRef.current) {
            window.removeEventListener('pointermove', listenersRef.current.move);
            window.removeEventListener('pointerup', listenersRef.current.end);
            window.removeEventListener('pointercancel', listenersRef.current.end);
            listenersRef.current = null;
        }

        if (!moved && isFromCreate) onClickRef.current?.();
    };

    const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
        if (!canReposition) return;
        event.preventDefault();
        event.stopPropagation();

        recalc();
        const m = metricsRef.current;
        if (!m) return;

        const layout = getCoverLayout(m.iw, m.ih, m.cw, m.ch);
        if (!layout.canDragX && !layout.canDragY) {
            onClickRef.current?.();
            return;
        }

        isDraggingRef.current = true;
        lockedAxisRef.current = null;
        dragStartRef.current = {
            x: event.clientX,
            y: event.clientY,
            tx: positionToTranslate(position.x, layout.overflowX),
            ty: positionToTranslate(position.y, layout.overflowY),
            moved: false,
            pos: { ...position },
        };
        setIsDragging(true);
        setDisplayPosition(position);

        const move = (e: PointerEvent) => {
            if (!isDraggingRef.current || !dragStartRef.current) return;

            const deltaX = e.clientX - dragStartRef.current.x;
            const deltaY = e.clientY - dragStartRef.current.y;

            if (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3) {
                dragStartRef.current.moved = true;
            }

            const metricsNow = metricsRef.current;
            if (!metricsNow) return;

            const layoutNow = getCoverLayout(
                metricsNow.iw,
                metricsNow.ih,
                metricsNow.cw,
                metricsNow.ch,
            );

            // Lock to a single axis for this gesture (no free diagonal drag).
            if (!lockedAxisRef.current && dragStartRef.current.moved) {
                if (layoutNow.canDragX && layoutNow.canDragY) {
                    lockedAxisRef.current = Math.abs(deltaX) >= Math.abs(deltaY) ? 'x' : 'y';
                } else if (layoutNow.canDragX) {
                    lockedAxisRef.current = 'x';
                } else if (layoutNow.canDragY) {
                    lockedAxisRef.current = 'y';
                }
            }

            const axis = lockedAxisRef.current;
            if (!axis) return;

            let nextX = dragStartRef.current.pos.x;
            let nextY = dragStartRef.current.pos.y;

            if (axis === 'x' && layoutNow.canDragX) {
                const nextTx = clamp(
                    dragStartRef.current.tx + deltaX,
                    -layoutNow.overflowX,
                    0,
                );
                nextX = translateToPosition(nextTx, layoutNow.overflowX);
            }

            if (axis === 'y' && layoutNow.canDragY) {
                const nextTy = clamp(
                    dragStartRef.current.ty + deltaY,
                    -layoutNow.overflowY,
                    0,
                );
                nextY = translateToPosition(nextTy, layoutNow.overflowY);
            }

            const next: BannerPosition = { x: nextX, y: nextY };
            setDisplayPosition(next);
            onChangeRef.current?.(fileNameRef.current, next);
        };

        const end = () => endDrag();
        listenersRef.current = { move, end };

        window.addEventListener('pointermove', move);
        window.addEventListener('pointerup', end);
        window.addEventListener('pointercancel', end);
    };

    const layout = metrics
        ? getCoverLayout(metrics.iw, metrics.ih, metrics.cw, metrics.ch)
        : null;
    const translateX = layout ? positionToTranslate(activePosition.x, layout.overflowX) : 0;
    const translateY = layout ? positionToTranslate(activePosition.y, layout.overflowY) : 0;

    const cursorClass = (() => {
        if (!canReposition || !layout) return '';
        if (isDragging) return 'cursor-grabbing';
        if (layout.canDragX && !layout.canDragY) return 'cursor-ew-resize';
        if (layout.canDragY && !layout.canDragX) return 'cursor-ns-resize';
        if (layout.canDragX || layout.canDragY) return 'cursor-grab';
        return 'cursor-pointer';
    })();

    // Read-only (listing / creator preview): CSS cover always fills the slide — no slick width races.
    if (!canReposition) {
        return (
            <div className="relative z-[2] h-40 w-full min-w-0 overflow-hidden bg-gray/25">
                {imageSrc && (
                    <img
                        src={imageSrc}
                        alt="Campaign Banner"
                        draggable={false}
                        onError={handleImageError}
                        className="h-full w-full object-cover pointer-events-none select-none"
                        style={{ objectPosition: `${position.x}% ${position.y}%` }}
                    />
                )}
            </div>
        );
    }

    return (
        <div
            ref={containerRef}
            className={`relative z-[2] h-40 w-full min-w-0 overflow-hidden bg-gray/25 touch-none ${cursorClass}`}
            onPointerDown={handlePointerDown}
            onClick={(event) => event.stopPropagation()}
        >
            {imageSrc && (
                <img
                    ref={imageRef}
                    src={imageSrc}
                    alt="Campaign Banner"
                    draggable={false}
                    onLoad={() => recalc()}
                    onError={handleImageError}
                    className="absolute left-0 top-0 block max-w-none max-h-none pointer-events-none select-none"
                    style={{
                        width: layout ? `${layout.scaledW}px` : '100%',
                        height: layout ? `${layout.scaledH}px` : '100%',
                        objectFit: layout ? undefined : 'cover',
                        transform: layout
                            ? `translate(${translateX}px, ${translateY}px)`
                            : undefined,
                        transformOrigin: 'top left',
                    }}
                />
            )}
        </div>
    );
};

export default CampaignBannerSlide;
