import React, { memo, useEffect, useMemo, useRef, useState } from 'react';
import { Dialog } from '@/components/elements/dialog';
import Input from '@/components/elements/Input';
import { Button } from '@/components/elements/button/index';
import GreyRowBox from '@/components/elements/GreyRowBox';
import type { BetterFilesCollaborationSession, BetterFilesCollaborationUser } from '../../api/server/files/collaboration';
import type { BetterFilesCollaborationPresence } from '../hooks/useLiveCollaboration';

interface Props {
    open: boolean;
    onClose: () => void;
    session: BetterFilesCollaborationSession | null;
    currentUser: BetterFilesCollaborationUser | null;
    members: BetterFilesCollaborationUser[];
    sessions: BetterFilesCollaborationSession[];
    presence: Record<string, BetterFilesCollaborationPresence>;
    enabled: boolean;
    settingsLoaded: boolean;
    settingsError: string | null;
    canStartEdit: boolean;
    onRefreshSessions: () => Promise<void>;
    onStart: (mode: 'view' | 'edit') => Promise<void>;
    onJoin: (code: string) => Promise<void>;
    onLeave: (closeForEveryone?: boolean) => Promise<void>;
}

const basename = (path: string) => path.split('/').filter(Boolean).pop() || path;
const getUserKey = (user: BetterFilesCollaborationUser) => user.connection_id || user.uuid;
const userSignature = (user: BetterFilesCollaborationUser) => `${getUserKey(user)}:${user.uuid}:${user.username}:${user.image}`;

const areSameUsers = (left: BetterFilesCollaborationUser[], right: BetterFilesCollaborationUser[]) => {
    if (left.length !== right.length) return false;
    return left.every((user, index) => userSignature(user) === userSignature(right[index]));
};

const stabilizeUsers = (
    previous: BetterFilesCollaborationUser[],
    next: BetterFilesCollaborationUser[]
) => {
    const previousKeys = previous.map(getUserKey);
    const nextByKey = new Map(next.map((user) => [getUserKey(user), user]));
    const ordered = previousKeys
        .map((key) => nextByKey.get(key))
        .filter(Boolean) as BetterFilesCollaborationUser[];

    next.forEach((user) => {
        if (!previousKeys.includes(getUserKey(user))) {
            ordered.push(user);
        }
    });

    return ordered;
};

const UserPill = memo(({ user, presence }: { user: BetterFilesCollaborationUser; presence?: BetterFilesCollaborationPresence }) => (
    <div className='flex min-w-0 items-center gap-2'>
        <img src={user.image} alt='' className='h-6 w-6 shrink-0 rounded-full bg-neutral-700' />
        <div className='min-w-0'>
            <p className='truncate text-sm text-neutral-200'>{user.username}</p>
            {presence && (
                <p className='truncate text-[10px] text-neutral-500'>
                    {basename(presence.path)} · line {presence.line}
                </p>
            )}
        </div>
    </div>
));

UserPill.displayName = 'LiveCollaborationUserPill';

const LiveCollaborationDialog: React.FC<Props> = ({
    open,
    onClose,
    session,
    currentUser,
    members,
    sessions,
    presence,
    enabled,
    settingsLoaded,
    settingsError,
    canStartEdit,
    onRefreshSessions,
    onStart,
    onJoin,
    onLeave,
}) => {
    const [code, setCode] = useState('');
    const [loading, setLoading] = useState(false);
    const [visiblePresence, setVisiblePresence] = useState(presence);
    const [visibleMembers, setVisibleMembers] = useState(members);
    const didRefreshOnOpenRef = useRef(false);
    const refreshSessionsRef = useRef(onRefreshSessions);
    const canCloseSession = !!session && !!currentUser && session.owner_uuid === currentUser.uuid;
    const startDescription = !settingsLoaded
        ? 'Checking live collaboration settings...'
        : settingsError
            ? settingsError
            : !enabled
                ? 'Live collaboration is disabled in Better Files admin settings.'
                : canStartEdit
                    ? 'Starts a temporary server-scoped live session. The join code is not a permission bypass.'
                    : 'View-only sessions are available. Editable sessions require the admin toggle and file update permission.';
    const memberItems = useMemo(() => visibleMembers.map((member) => ({
        user: member,
        key: getUserKey(member),
    })), [visibleMembers]);
    const activeSessions = useMemo(() => sessions.filter((item) => item.code !== session?.code), [session?.code, sessions]);

    useEffect(() => {
        refreshSessionsRef.current = onRefreshSessions;
    }, [onRefreshSessions]);

    useEffect(() => {
        if (!open) {
            didRefreshOnOpenRef.current = false;
            return;
        }

        if (didRefreshOnOpenRef.current) return;
        didRefreshOnOpenRef.current = true;
        void refreshSessionsRef.current();
    }, [open]);

    useEffect(() => {
        if (!open) {
            setVisiblePresence(presence);
            return;
        }

        const timer = window.setTimeout(() => {
            setVisiblePresence(presence);
        }, 180);

        return () => window.clearTimeout(timer);
    }, [open, presence]);

    useEffect(() => {
        if (!open) {
            setVisibleMembers(members);
            return;
        }

        const timer = window.setTimeout(() => {
            setVisibleMembers((current) => {
                const next = stabilizeUsers(current, members);
                return areSameUsers(current, next) ? current : next;
            });
        }, 120);

        return () => window.clearTimeout(timer);
    }, [members, open]);

    const run = async (action: () => Promise<void>) => {
        setLoading(true);
        try {
            await action();
        } finally {
            setLoading(false);
        }
    };

    if (!open) return null;

    return (
        <Dialog
            open={open}
            onClose={onClose}
            title='Live Session'
            description='Share editing presence with users who already have access to this server.'
        >
            <div className='mt-4 space-y-3'>
                {session ? (
                    <>
                        <GreyRowBox className='flex-col !items-stretch' $hoverable={false}>
                            <div className='flex items-center justify-between gap-3'>
                                <div className='min-w-0'>
                                    <p className='text-xs uppercase tracking-wide text-neutral-500'>Session Code</p>
                                    <p className='mt-1 font-mono text-lg font-semibold text-neutral-100'>{session.code}</p>
                                </div>
                                <span className='rounded bg-neutral-600 px-2 py-1 text-xs font-semibold uppercase text-neutral-300'>
                                    {session.mode}
                                </span>
                            </div>
                        </GreyRowBox>
                        <GreyRowBox className='flex-col !items-stretch' $hoverable={false}>
                            <p className='mb-2 text-xs uppercase tracking-wide text-neutral-500'>
                                Users ({memberItems.length}/{session.max_users})
                            </p>
                            <div className='min-h-[4.25rem] space-y-2'>
                                {memberItems.map((member) => (
                                    <UserPill key={member.key} user={member.user} presence={visiblePresence[member.key]} />
                                ))}
                            </div>
                        </GreyRowBox>
                    </>
                ) : (
                    <>
                        <GreyRowBox className='flex-col !items-stretch' $hoverable={false}>
                            <p className='text-sm font-semibold text-neutral-100'>Start a session</p>
                            <p className='mt-1 text-xs text-neutral-400'>
                                {startDescription}
                            </p>
                            <div className='mt-3 flex flex-wrap gap-2'>
                                <Button type='button' size={Button.Sizes.Small} disabled={loading || !settingsLoaded || !enabled || !canStartEdit} onClick={() => run(() => onStart('edit'))}>
                                    Edit Session
                                </Button>
                                <Button.Text type='button' size={Button.Sizes.Small} disabled={loading || !settingsLoaded || !enabled} onClick={() => run(() => onStart('view'))}>
                                    View Only
                                </Button.Text>
                            </div>
                        </GreyRowBox>
                        {settingsLoaded && enabled && activeSessions.length > 0 && (
                            <GreyRowBox className='flex-col !items-stretch' $hoverable={false}>
                                <p className='text-sm font-semibold text-neutral-100'>Active sessions</p>
                                <p className='mt-1 text-xs text-neutral-400'>Join a session already open on this server.</p>
                                <div className='mt-3 space-y-2'>
                                    {activeSessions.map((item) => (
                                        <button
                                            key={item.code}
                                            type='button'
                                            disabled={loading}
                                            onClick={() => run(() => onJoin(item.code))}
                                            className='flex w-full items-center justify-between gap-3 rounded border border-neutral-600 bg-neutral-800 px-3 py-2 text-left transition-colors hover:border-neutral-500 hover:bg-neutral-700 disabled:cursor-not-allowed disabled:opacity-50'
                                        >
                                            <span className='min-w-0'>
                                                <span className='block font-mono text-sm font-semibold text-neutral-100'>{item.code}</span>
                                                <span className='block truncate text-[10px] uppercase tracking-wide text-neutral-500'>
                                                    {item.mode} · {item.max_users} users max
                                                </span>
                                            </span>
                                            <span className='shrink-0 text-xs font-semibold text-neutral-300'>Join</span>
                                        </button>
                                    ))}
                                </div>
                            </GreyRowBox>
                        )}
                        <GreyRowBox className='flex-col !items-stretch' $hoverable={false}>
                            <p className='text-sm font-semibold text-neutral-100'>Join by code</p>
                            <div className='mt-3 flex gap-2'>
                                <Input
                                    value={code}
                                    onChange={(e) => setCode(e.currentTarget.value)}
                                    placeholder='ABCD-1234'
                                    autoFocus={open && !session}
                                    className='font-mono uppercase'
                                />
                                <Button.Text
                                    type='button'
                                    size={Button.Sizes.Small}
                                    disabled={loading || !settingsLoaded || !enabled || !code.trim()}
                                    onClick={() => run(async () => {
                                        await onJoin(code);
                                        setCode('');
                                    })}
                                >
                                    Join
                                </Button.Text>
                            </div>
                        </GreyRowBox>
                    </>
                )}
            </div>
            <Dialog.Footer>
                {canCloseSession && (
                    <Button.Danger type='button' onClick={() => run(() => onLeave(true))} disabled={loading}>
                        Close Session
                    </Button.Danger>
                )}
                {session && (
                    <Button.Text type='button' onClick={() => run(() => onLeave(false))} disabled={loading}>
                        Leave
                    </Button.Text>
                )}
                <Button.Text type='button' onClick={onClose}>
                    Done
                </Button.Text>
            </Dialog.Footer>
        </Dialog>
    );
};

export default memo(LiveCollaborationDialog);
