import React, { useCallback, useEffect, useState } from 'react';
import { ChevronUpIcon, DocumentIcon, FolderIcon, HomeIcon } from '@heroicons/react/outline';
import GreyRowBox from '@/components/elements/GreyRowBox';
import Input from '@/components/elements/Input';
import Spinner from '@/components/elements/Spinner';
import Tooltip from '@/components/elements/tooltip/Tooltip';
import { Button } from '@/components/elements/button/index';
import { Dialog } from '@/components/elements/dialog';
import { browse } from '../../../api/server/importer';
import { BrowseRequest, RemoteItem } from '../../../api/server/importer/browse';

interface Props {
    open: boolean;
    uuid: string;
    connectionData: BrowseRequest;
    selectMode: 'path' | 'items';
    onClose: () => void;
    onSelectPath: (path: string) => void;
    onSelectItems: (items: string[], basePath: string) => void;
}

const formatSize = (bytes: number): string => {
    if (bytes < 1024) return `${bytes} B`;
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
    if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;

    return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
};

const parentPath = (path: string): string => {
    const clean = path.replace(/\/+$/, '');

    if (!clean || clean === '/') return '/';

    return clean.slice(0, clean.lastIndexOf('/')) || '/';
};

const normalizePath = (path: string): string => {
    const normalized = `/${path.replace(/^\/+/, '')}`.replace(/\/+/g, '/').replace(/\/+$/, '');

    return normalized || '/';
};

const commonBasePath = (paths: string[]): string => {
    if (paths.length === 0) return '/';

    const normalized = paths.map(normalizePath);
    const firstParts = normalized[0] === '/' ? [] : normalized[0].replace(/^\//, '').split('/');
    const common: string[] = [];

    for (let index = 0; index < firstParts.length; index++) {
        const part = firstParts[index];

        if (normalized.every((path) => {
            const parts = path === '/' ? [] : path.replace(/^\//, '').split('/');

            return parts[index] === part;
        })) {
            common.push(part);
        } else {
            break;
        }
    }

    return common.length === 0 ? '/' : `/${common.join('/')}`;
};

export default ({ open, uuid, connectionData, selectMode, onClose, onSelectPath, onSelectItems }: Props) => {
    const [items, setItems] = useState<RemoteItem[]>([]);
    const [currentPath, setCurrentPath] = useState(connectionData.path || '/');
    const [selected, setSelected] = useState<Set<string>>(new Set());
    const [selectedBases, setSelectedBases] = useState<Map<string, string>>(new Map());
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const connectionHost = connectionData.host || connectionData.hote;

    const load = useCallback(
        async (path: string) => {
            setLoading(true);
            setError(null);
            setItems([]);

            try {
                const response = await browse(uuid, { ...connectionData, path });
                const normalized = response.items.map((item) => {
                    const isDirectory = item.is_directory === true || item.type === 'directory';

                    return {
                        ...item,
                        type: isDirectory ? 'directory' : 'file',
                        is_directory: isDirectory,
                        is_file: !isDirectory,
                    } as RemoteItem;
                });
                const sorted = normalized.sort((a, b) => {
                    if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;

                    return a.name.localeCompare(b.name);
                });

                setItems(sorted);
                setCurrentPath(response.path || path);
            } catch (e: any) {
                setItems([]);
                setError(e?.message || 'Failed to browse the remote directory.');
            } finally {
                setLoading(false);
            }
        },
        [connectionData, uuid]
    );

    useEffect(() => {
        if (!open) return;

        setSelected(new Set());
        setSelectedBases(new Map());
        load(connectionData.path || '/');
    }, [connectionData.path, load, open]);

    const navigate = (path: string) => {
        if (selectMode === 'path') {
            setSelected(new Set());
        }

        load(path);
    };

    const toggle = (path: string) => {
        setSelected((current) => {
            const next = new Set(current);

            if (next.has(path)) {
                next.delete(path);
                setSelectedBases((currentBases) => {
                    const nextBases = new Map(currentBases);

                    nextBases.delete(path);

                    return nextBases;
                });
            } else {
                next.add(path);
                setSelectedBases((currentBases) => {
                    const nextBases = new Map(currentBases);

                    nextBases.set(path, currentPath);

                    return nextBases;
                });
            }

            return next;
        });
    };

    const confirm = () => {
        if (selectMode === 'items') {
            const selectedPaths = Array.from(selected);
            const basePath = commonBasePath(
                selectedPaths.map((path) => selectedBases.get(path) || parentPath(path))
            );

            onSelectItems(selectedPaths, basePath);
        } else {
            onSelectPath(currentPath);
        }

        onClose();
    };

    return (
        <Dialog
            open={open}
            onClose={onClose}
            title={'Remote File Browser'}
            description={`Connected to ${connectionData.user}@${connectionHost}:${connectionData.port}`}
        >
            <div className={'mt-4 space-y-3'}>
                <GreyRowBox $hoverable={false} className={'!p-3'}>
                    <Tooltip content={'Root directory'} placement={'top'}>
                        <Button.Text
                            type={'button'}
                            size={Button.Sizes.Small}
                            shape={Button.Shapes.IconSquare}
                            variant={Button.Variants.Secondary}
                            onClick={() => navigate('/')}
                        >
                            <HomeIcon className={'h-4 w-4'} />
                        </Button.Text>
                    </Tooltip>
                    <p className={'mx-3 min-w-0 flex-1 truncate font-mono text-xs text-neutral-300'}>{currentPath}</p>
                    <Tooltip content={'Parent directory'} placement={'top'}>
                        <Button.Text
                            type={'button'}
                            size={Button.Sizes.Small}
                            shape={Button.Shapes.IconSquare}
                            variant={Button.Variants.Secondary}
                            onClick={() => navigate(parentPath(currentPath))}
                        >
                            <ChevronUpIcon className={'h-4 w-4'} />
                        </Button.Text>
                    </Tooltip>
                </GreyRowBox>

                <div className={'max-h-96 overflow-y-auto pr-1'}>
                    {loading ? (
                        <GreyRowBox $hoverable={false} className={'!p-4'}>
                            <Spinner size={'small'} />
                            <p className={'ml-3 text-sm text-neutral-400'}>Loading remote directory...</p>
                        </GreyRowBox>
                    ) : error ? (
                        <GreyRowBox $hoverable={false} className={'!p-4'}>
                            <p className={'text-sm text-red-400'}>{error}</p>
                        </GreyRowBox>
                    ) : items.length === 0 && !loading ? (
                        <GreyRowBox $hoverable={false} className={'!p-4'}>
                            <p className={'text-sm text-neutral-400'}>This directory is empty.</p>
                        </GreyRowBox>
                    ) : (
                        <div className={'space-y-2'}>
                            {items.map((item) => {
                                const itemSelected = selected.has(item.path);
                                const isDirectory = item.is_directory === true || item.type === 'directory';

                                return (
                                    <GreyRowBox
                                        key={item.path}
                                        className={`!p-3 ${itemSelected ? '!border-neutral-500 !bg-neutral-600' : ''} ${
                                            selectMode === 'path' && isDirectory ? 'cursor-pointer' : ''
                                        }`}
                                        onClick={() => {
                                            if (selectMode === 'path' && isDirectory) {
                                                navigate(item.path);
                                            } else if (selectMode === 'items') {
                                                toggle(item.path);
                                            }
                                        }}
                                    >
                                        {selectMode === 'items' && (
                                            <Input
                                                type={'checkbox'}
                                                checked={itemSelected}
                                                onChange={() => toggle(item.path)}
                                                onClick={(e) => e.stopPropagation()}
                                                className={'mr-3'}
                                            />
                                        )}
                                        {isDirectory ? (
                                            <FolderIcon className={'mr-3 h-5 w-5 shrink-0 text-blue-400'} />
                                        ) : (
                                            <DocumentIcon className={'mr-3 h-5 w-5 shrink-0 text-neutral-500'} />
                                        )}
                                        <div className={'min-w-0 flex-1'}>
                                            <p className={'truncate text-sm text-neutral-100'}>{item.name}</p>
                                            <p className={'mt-1 text-xs text-neutral-500'}>
                                                {isDirectory ? 'Directory' : formatSize(item.size)}
                                            </p>
                                        </div>
                                        {selectMode === 'items' && isDirectory && (
                                            <Button.Text
                                                type={'button'}
                                                size={Button.Sizes.Small}
                                                variant={Button.Variants.Secondary}
                                                onClick={(e) => {
                                                    e.stopPropagation();
                                                    navigate(item.path);
                                                }}
                                            >
                                                Open
                                            </Button.Text>
                                        )}
                                    </GreyRowBox>
                                );
                            })}
                        </div>
                    )}
                </div>

                {selectMode === 'items' && (
                    <div className={'flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between'}>
                        <p className={'text-xs text-neutral-500'}>
                            {selected.size === 0
                                ? 'Select one or more remote items to import.'
                                : `${selected.size} item(s) selected. Selections are kept while browsing.`}
                        </p>
                        {selected.size > 0 && (
                            <Button.Text
                                type={'button'}
                                size={Button.Sizes.Small}
                                onClick={() => {
                                    setSelected(new Set());
                                    setSelectedBases(new Map());
                                }}
                            >
                                Clear Selection
                            </Button.Text>
                        )}
                    </div>
                )}
            </div>
            <Dialog.Footer>
                <Button.Text type={'button'} onClick={onClose}>
                    Cancel
                </Button.Text>
                <Button type={'button'} onClick={confirm} disabled={selectMode === 'items' && selected.size === 0}>
                    {selectMode === 'items' ? `Import ${selected.size} Item(s)` : 'Use This Path'}
                </Button>
            </Dialog.Footer>
        </Dialog>
    );
};
