import React from 'react';
import styled from 'styled-components/macro';
import { themeColors } from './colorExtractor';
import { colors } from './colors';
import FileIcon from './FileIcon';
import GreyRowBox from '@/components/elements/GreyRowBox';
import type { SimpleEditorViewState } from './SimpleEditor';

export interface OpenFile {
    path: string;
    name: string;
    content: string;
    originalContent: string;
    hasUnsavedChanges: boolean;
    editorViewState?: SimpleEditorViewState;
}

interface EditorTabsProps {
    openFiles: OpenFile[];
    activeMainFilePath: string | null;
    activeSplitFilePath?: string | null;
    activePane?: 'main' | 'split';
    splitView?: boolean;
    onTabClick: (path: string, targetPane: 'main' | 'split') => void;
    onTabClose: (path: string) => void;
    onTabDragStart?: (path: string, e: React.DragEvent) => void;
    onTabsReorder?: (draggedPath: string, targetPath: string, placement: 'before' | 'after') => void;
    disableDrag?: boolean;
    chrome?: boolean;
    connected?: boolean;
    className?: string;
    style?: React.CSSProperties;
}

const cx = (...parts: Array<string | false | null | undefined>) => parts.filter(Boolean).join(' ');

const TabsScroller = styled.div`
    width: 100%;
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior: contain;
`;

const EditorTabs: React.FC<EditorTabsProps> = ({
    openFiles,
    activeMainFilePath,
    activeSplitFilePath,
    activePane = 'main',
    splitView = false,
    onTabClick,
    onTabClose,
    onTabDragStart,
    onTabsReorder,
    disableDrag = false,
    chrome = true,
    connected = false,
    className,
    style,
}) => {
    const tabsScrollerRef = React.useRef<HTMLDivElement | null>(null);
    const [tabsScrollerElement, setTabsScrollerElement] = React.useState<HTMLDivElement | null>(null);
    const [draggedTabPath, setDraggedTabPath] = React.useState<string | null>(null);
    const [dropTarget, setDropTarget] = React.useState<{ path: string; placement: 'before' | 'after' } | null>(null);
    const tabRefs = React.useRef<Record<string, HTMLDivElement | null>>({});
    const previousTabRectsRef = React.useRef<Map<string, DOMRect>>(new Map());
    const lastLiveReorderRef = React.useRef<string | null>(null);
    const scrollTargetRef = React.useRef(0);
    const scrollFrameRef = React.useRef<number | null>(null);

    const setTabsScrollerRef = React.useCallback((node: HTMLDivElement | null) => {
        tabsScrollerRef.current = node;
        setTabsScrollerElement(node);
    }, []);

    React.useEffect(() => {
        return () => {
            if (scrollFrameRef.current !== null) {
                cancelAnimationFrame(scrollFrameRef.current);
            }
        };
    }, []);

    const animateTabsScroll = React.useCallback((scroller: HTMLDivElement) => {
        if (scrollFrameRef.current !== null) return;

        const step = () => {
            const current = scroller.scrollLeft;
            const diff = scrollTargetRef.current - current;

            if (Math.abs(diff) < 0.5) {
                scroller.scrollLeft = scrollTargetRef.current;
                scrollFrameRef.current = null;
                return;
            }

            scroller.scrollLeft = current + diff * 0.28;
            scrollFrameRef.current = requestAnimationFrame(step);
        };

        scrollFrameRef.current = requestAnimationFrame(step);
    }, []);

    const handleTabsWheel = React.useCallback((event: WheelEvent) => {
        const scroller = tabsScrollerRef.current;
        if (!scroller) return;

        const canScroll = scroller.scrollWidth > scroller.clientWidth;
        if (!canScroll) return;

        const delta = Math.abs(event.deltaY) >= Math.abs(event.deltaX) ? event.deltaY : event.deltaX;
        if (delta === 0) return;

        event.preventDefault();
        event.stopPropagation();

        const deltaMultiplier = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? scroller.clientWidth : 1;
        const max = scroller.scrollWidth - scroller.clientWidth;
        const currentTarget = scrollFrameRef.current === null ? scroller.scrollLeft : scrollTargetRef.current;
        const next = currentTarget + delta * deltaMultiplier;
        const clamped = Math.max(0, Math.min(max, next));

        scrollTargetRef.current = clamped;
        animateTabsScroll(scroller);
    }, [animateTabsScroll]);

    const getDropPlacement = React.useCallback((event: React.DragEvent<HTMLElement>) => {
        const rect = event.currentTarget.getBoundingClientRect();
        return event.clientX < rect.left + rect.width / 2 ? 'before' : 'after';
    }, []);

    const rememberTabRects = React.useCallback(() => {
        previousTabRectsRef.current = new Map(
            Object.entries(tabRefs.current)
                .filter(([, element]) => !!element)
                .map(([path, element]) => [path, element!.getBoundingClientRect()])
        );
    }, []);

    const reorderTabsLive = React.useCallback((draggedPath: string, targetPath: string, placement: 'before' | 'after') => {
        if (!onTabsReorder || draggedPath === targetPath) return;

        const draggedIndex = openFiles.findIndex((file) => file.path === draggedPath);
        const targetIndex = openFiles.findIndex((file) => file.path === targetPath);
        if (draggedIndex === -1 || targetIndex === -1) return;

        const alreadyBefore = placement === 'before' && draggedIndex === targetIndex - 1;
        const alreadyAfter = placement === 'after' && draggedIndex === targetIndex + 1;
        if (alreadyBefore || alreadyAfter) return;

        const reorderKey = `${draggedPath}:${targetPath}:${placement}`;
        if (lastLiveReorderRef.current === reorderKey) return;
        lastLiveReorderRef.current = reorderKey;

        rememberTabRects();
        onTabsReorder(draggedPath, targetPath, placement);
    }, [onTabsReorder, openFiles, rememberTabRects]);

    const clearTabDropState = React.useCallback(() => {
        setDraggedTabPath(null);
        setDropTarget(null);
        lastLiveReorderRef.current = null;
    }, []);

    const setTabRef = React.useCallback((path: string, element: HTMLDivElement | null) => {
        tabRefs.current[path] = element;
    }, []);

    React.useLayoutEffect(() => {
        const previousRects = previousTabRectsRef.current;
        if (previousRects.size === 0) return;

        openFiles.forEach((file) => {
            const element = tabRefs.current[file.path];
            const previous = previousRects.get(file.path);
            if (!element || !previous) return;

            const next = element.getBoundingClientRect();
            const deltaX = previous.left - next.left;
            const deltaY = previous.top - next.top;
            if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1) return;

            element.animate(
                [
                    { transform: `translate3d(${deltaX}px, ${deltaY}px, 0)` },
                    { transform: 'translate3d(0, 0, 0)' },
                ],
                {
                    duration: 170,
                    easing: 'cubic-bezier(0.2, 0, 0, 1)',
                }
            );
        });

        previousTabRectsRef.current = new Map();
    }, [openFiles]);

    React.useEffect(() => {
        if (!tabsScrollerElement) return;

        tabsScrollerElement.addEventListener('wheel', handleTabsWheel, { passive: false });

        return () => {
            tabsScrollerElement.removeEventListener('wheel', handleTabsWheel);
        };
    }, [handleTabsWheel, tabsScrollerElement]);

    if (openFiles.length === 0) {
        return (
            <TabsScroller
                ref={setTabsScrollerRef}
                className={cx(
                    'flex items-end px-0 justify-start w-full max-w-full min-w-0',
                    chrome && 'bg-gray-600 border-b border-gray-500',
                    className
                )}
                style={{
                    minHeight: 28,
                    paddingLeft: 0,
                    paddingRight: 0,
                    borderBottom: connected ? `1px solid ${themeColors.page.secondaryHover}` : undefined,
                    paddingBottom: connected ? 0 : undefined,
                    ...style,
                }}
            >
                <div className="flex-1 flex items-center justify-center text-sm text-neutral-300 h-10">
                    No files open
                </div>
            </TabsScroller>
        );
    }

    const effectivePane: 'main' | 'split' =
        splitView && activePane === 'split' && activeSplitFilePath ? 'split' : 'main';

    return (
        <TabsScroller
            ref={setTabsScrollerRef}
            className={cx(
                'flex items-end px-0 justify-start w-full max-w-full min-w-0',
                chrome && 'bg-gray-600 border-b border-gray-500',
                className
            )}
            style={{
                minHeight: 28,
                paddingLeft: 0,
                paddingRight: 0,
                borderBottom: connected ? `1px solid ${themeColors.page.secondaryHover}` : undefined,
                paddingBottom: connected ? 0 : undefined,
                ...style,
            }}
        >
            {openFiles.map((file) => {
                const isMainActive = file.path === activeMainFilePath;
                const isSplitActive = splitView && !!activeSplitFilePath && file.path === activeSplitFilePath;
                const isFocused = effectivePane === 'split' ? isSplitActive : isMainActive;
                const isOpenInActivePane = isMainActive || isSplitActive;
                const dropPlacement = dropTarget?.path === file.path ? dropTarget.placement : null;

                const baseClasses = cx(
                    'flex items-center gap-1 text-xs cursor-pointer transition-all duration-150 relative select-none flex-shrink-0',
                    'hover:!bg-gray-700 hover:text-neutral-100 hover:!border-gray-600',
                    isFocused ? 'text-neutral-100 !border-gray-500' : isOpenInActivePane ? 'text-neutral-200 !border-gray-600' : 'text-neutral-300 !border-gray-700'
                );

                const activeClasses = isFocused ? 'font-semibold' : '';

                const tabStyle: React.CSSProperties = {
                    padding: '6px 9px',
                    margin: '0 1px',
                    minWidth: 90,
                    maxWidth: 160,
                    backgroundColor: isFocused
                        ? themeColors.page.secondaryActive
                        : isOpenInActivePane
                            ? themeColors.page.secondaryHover
                            : themeColors.page.secondary,
                    borderColor: isFocused
                        ? themeColors.page.secondarySelected
                        : isOpenInActivePane
                            ? themeColors.page.secondaryActive
                            : themeColors.page.secondaryHover,
                    boxShadow: isFocused ? `inset 0 -2px 0 ${colors.primary.default}` : undefined,
                };
                const tabMarginBottom = 0;

                return (
                    <GreyRowBox
                        ref={(element: HTMLDivElement | null) => setTabRef(file.path, element)}
                        key={file.path}
                        className={cx(
                            baseClasses,
                            activeClasses,
                            draggedTabPath === file.path && 'opacity-60',
                            'shadow-none'
                        )}
                        onClick={() => onTabClick(file.path, effectivePane)}
                        draggable={!disableDrag}
                        onDragStart={(e) => {
                            if (disableDrag) return;
                            setDraggedTabPath(file.path);
                            onTabDragStart?.(file.path, e);
                        }}
                        onDragEnd={clearTabDropState}
                        onDragOver={(e) => {
                            if (disableDrag || !draggedTabPath || draggedTabPath === file.path) return;
                            e.preventDefault();
                            e.stopPropagation();
                            e.dataTransfer.dropEffect = 'move';
                            const placement = getDropPlacement(e);
                            setDropTarget((current) =>
                                current?.path === file.path && current.placement === placement ? current : { path: file.path, placement }
                            );
                            reorderTabsLive(draggedTabPath, file.path, placement);
                        }}
                        onDragLeave={() => {
                            setDropTarget((current) => current?.path === file.path ? null : current);
                        }}
                        onDrop={(e) => {
                            if (disableDrag || !draggedTabPath || draggedTabPath === file.path) return;
                            e.preventDefault();
                            e.stopPropagation();
                            const placement = getDropPlacement(e);
                            rememberTabRects();
                            onTabsReorder?.(draggedTabPath, file.path, placement);
                            clearTabDropState();
                        }}
                        style={{
                            ...tabStyle,
                            marginBottom: tabMarginBottom,
                        }}
                    >
                        {dropPlacement && (
                            <span
                                className='absolute top-1 bottom-1 z-10 rounded-full'
                                style={{
                                    [dropPlacement === 'before' ? 'left' : 'right']: -3,
                                    width: 3,
                                    background: colors.primary.default,
                                    boxShadow: `0 0 8px ${colors.primary.default}`,
                                }}
                            />
                        )}
                        {file.hasUnsavedChanges && (
                            <span
                                className="absolute"
                                style={{
                                    top: 4,
                                    right: 4,
                                    width: 6,
                                    height: 6,
                                    borderRadius: '50%',
                                    background: colors.primary.default,
                                    boxShadow: `0 0 6px ${colors.primary.default}`,
                                }}
                            />
                        )}
                        <span className="flex-shrink-0 flex items-center justify-center" style={{ width: 16, height: 16 }}>
                            <FileIcon filename={file.name} />
                        </span>
                        <span className={cx('truncate flex-1 text-xs leading-none', isFocused ? 'font-semibold' : 'font-medium')} title={file.path}>
                            {file.name}
                        </span>
                        <button
                            onClick={(e) => {
                                e.stopPropagation();
                                onTabClose(file.path);
                            }}
                            title="Close"
                            className="p-0.5 transition-colors flex-shrink-0 flex items-center justify-center text-neutral-300 hover:bg-red-500 hover:text-red-300 leading-none"
                            style={{ width: 16, height: 16, borderRadius: 4 }}
                        >
                            <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                            </svg>
                        </button>
                    </GreyRowBox>
                );
            })}
        </TabsScroller>
    );
};

export default EditorTabs;
