import { AddLinkEventTracker, CreateLinkEvent, GetGuestRoomResultEvent, ILinkEventTracker, GetRoomEngine, RateFlatMessageComposer, RemoveLinkEventTracker, RoomEngineEvent, RoomGeometry } from '@octane/renderer';
import { AnimatePresence, motion } from 'framer-motion';
import { FC, useEffect, useEffectEvent, useRef, useState } from 'react';
import { GetConfigurationValue, LocalizeText, SendMessageComposer, SetLocalStorage, TryVisitRoom } from '../../../../api';
import { Text } from '../../../../common';
import { localizeWithFallback } from '../../../../api/utils/localizeWithFallback';
import { useAchievements, useMessageEvent, useNavigatorData, useOctaneEvent, useRoom } from '../../../../hooks';
import { classNames } from '../../../../layout';
import { getRegisteredPlugins, IOctanePlugin, subscribePlugins } from '../../../plugins/OctanePluginApi';
import { applyRoomZoom } from './roomZoom.helpers';
import { BaseButton } from '../../../../common/BaseButton';
import { Hint } from '../../../../common/Tooltip';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';

interface RoomHistoryEntry {
    roomId: number;
    roomName: string;
}

interface RoomEnterInfo {
    roomId: number;
    roomName: string;
    ownerLine: string;
    tags: string[];
}

const ROOM_INFO_TAG_MAX = 16;
const ROOM_INFO_TAG_COUNT = 2;

// Habbo shortens tags on the entry card to 16 characters.
const trimRoomTag = (tag: string) => (tag.length > ROOM_INFO_TAG_MAX ? `${tag.slice(0, ROOM_INFO_TAG_MAX)}...` : tag);

const ROOM_HISTORY_KEY = 'nitro.room.history';
const ROOM_HISTORY_MAX = 10;
const ROOM_NAME_MAX = 80;
const ROOM_ZOOM_SCALES = [0.5, 1, 2, 4, 8, 16];

const readRoomHistory = (): RoomHistoryEntry[] => {
    try {
        const raw = window.localStorage.getItem(ROOM_HISTORY_KEY);
        if (!raw) return [];

        const parsed = JSON.parse(raw);
        if (!Array.isArray(parsed)) return [];

        return parsed
            .filter((entry) => entry && Number.isInteger(entry.roomId) && entry.roomId > 0 && typeof entry.roomName === 'string')
            .slice(-ROOM_HISTORY_MAX)
            .map((entry) => ({ roomId: entry.roomId, roomName: entry.roomName.slice(0, ROOM_NAME_MAX) }));
    } catch {
        return [];
    }
};

const getNearestZoomScale = (scale: number) => {
    if (Number.isNaN(scale) || scale <= 0) return 1;

    return ROOM_ZOOM_SCALES.reduce((nearest, current) => (Math.abs(current - scale) < Math.abs(nearest - scale) ? current : nearest), ROOM_ZOOM_SCALES[0]);
};

const getNextZoomScale = (scale: number, direction: number) => {
    const currentScale = getNearestZoomScale(scale);

    if (direction > 0) {
        return ROOM_ZOOM_SCALES.find((zoomScale) => zoomScale > currentScale + 0.001) ?? currentScale;
    }

    if (direction < 0) {
        return [...ROOM_ZOOM_SCALES].reverse().find((zoomScale) => zoomScale < currentScale - 0.001) ?? currentScale;
    }

    return currentScale;
};

const getZoomText = (scale: number) => (Math.round(Math.log(getNearestZoomScale(scale)) / Math.LN2) + 1).toString();

export const RoomToolsWidgetView: FC<{}> = (props) => {
    const [zoomScale, setZoomScale] = useState<number>(1);
    const [hasLikedRoom, setHasLikedRoom] = useState<boolean>(false);
    const [isToolsOpen, setIsToolsOpen] = useState<boolean>(true);
    const [isOpenHistory, setIsOpenHistory] = useState<boolean>(false);
    const [roomHistory, setRoomHistory] = useState<RoomHistoryEntry[]>([]);
    const [plugins, setPlugins] = useState<IOctanePlugin[]>([]);
    const [roomEnterInfo, setRoomEnterInfo] = useState<RoomEnterInfo>(null);
    const [isRoomInfoHovered, setIsRoomInfoHovered] = useState(false);
    const roomInfoTimerRef = useRef<ReturnType<typeof setTimeout>>(null);
    const { navigatorData } = useNavigatorData();
    const { hasWiredAchievements } = useAchievements();
    const { roomSession = null } = useRoom();

    useEffect(() => {
        setPlugins(getRegisteredPlugins());
        return subscribePlugins(() => setPlugins(getRegisteredPlugins()));
    }, []);

    const getLogicalZoomScale = () => {
        if (!roomSession) return 1;

        const displayScale = GetRoomEngine().getRoomInstanceRenderingCanvasScale(roomSession.roomId, 1);
        const geometry = GetRoomEngine().getRoomInstanceGeometry(roomSession.roomId, 1);
        const geometryRatio = geometry ? geometry.scale / RoomGeometry.SCALE_ZOOMED_IN : 1;

        return getNearestZoomScale(displayScale * geometryRatio);
    };

    const applyZoomScale = (logicalScale: number) => {
        if (!roomSession) return;

        applyRoomZoom(roomSession.roomId, logicalScale);
        setZoomScale(logicalScale);
    };

    const updateZoomScale = () => {
        if (!roomSession) return;

        setZoomScale(getLogicalZoomScale());
    };

    const zoomRoom = (direction: number) => {
        if (!roomSession || direction === 0) return;

        if (!GetConfigurationValue('room.zoom.enabled', true)) {
            const geometry = GetRoomEngine().getRoomInstanceGeometry(roomSession.roomId, 1);
            if (!geometry) return;

            if (direction > 0) geometry.performZoomIn();
            else geometry.performZoomOut();

            setZoomScale(direction > 0 ? 2 : 1);
            return;
        }

        const currentScale = getLogicalZoomScale();
        const nextScale = getNextZoomScale(currentScale, direction);

        if (Math.abs(nextScale - currentScale) <= 0.001) return;

        applyZoomScale(nextScale);
    };

    const handleToolClick = (action: string, value?: string) => {
        if (!roomSession) return;

        switch (action) {
            case 'achievements':
                CreateLinkEvent('questengine/achievements/wired_games');
                return;
            case 'settings':
                CreateLinkEvent('navigator/toggle-room-info');
                return;
            case 'zoom_in':
                zoomRoom(1);
                return;
            case 'zoom_out':
                zoomRoom(-1);
                return;
            case 'chat_history':
                CreateLinkEvent('chat-history/toggle');
                return;
            case 'like_room':
                if (hasLikedRoom) return;
                SendMessageComposer(new RateFlatMessageComposer(1));
                setHasLikedRoom(true);
                return;
            case 'toggle_room_link':
                CreateLinkEvent('navigator/toggle-room-link');
                return;
            case 'room_history':
                if (roomHistory.length > 0) setIsOpenHistory((prev) => !prev);
                return;
            case 'room_history_back':
                const prevIndex = roomHistory.findIndex((room) => room.roomId === navigatorData.currentRoomId) - 1;
                if (prevIndex >= 0) TryVisitRoom(roomHistory[prevIndex].roomId);
                return;
            case 'room_history_next':
                const nextIndex = roomHistory.findIndex((room) => room.roomId === navigatorData.currentRoomId) + 1;
                if (nextIndex < roomHistory.length) TryVisitRoom(roomHistory[nextIndex].roomId);
                return;
        }
    };

    const currentRoomHistoryIndex = navigatorData ? roomHistory.findIndex((room) => room.roomId === navigatorData.currentRoomId) : -1;
    const hasHistory = roomHistory.length > 0;
    const canGoBack = currentRoomHistoryIndex > 0;
    const canGoNext = currentRoomHistoryIndex !== -1 && currentRoomHistoryIndex < roomHistory.length - 1;

    const onChangeRoomHistory = (roomId: number, roomName: string) => {
        if (!Number.isInteger(roomId) || roomId <= 0) return;

        let newStorage = readRoomHistory();
        if (newStorage.some((room) => room.roomId === roomId)) return;

        if (newStorage.length >= ROOM_HISTORY_MAX) newStorage.shift();
        newStorage = [...newStorage, { roomId, roomName: (roomName || '').slice(0, ROOM_NAME_MAX) }];

        setRoomHistory(newStorage);
        SetLocalStorage(ROOM_HISTORY_KEY, newStorage);
    };

    useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, (event) => {
        const parser = event.getParser();
        if (!parser.roomEnter || parser.data.roomId !== roomSession.roomId) return;

        onChangeRoomHistory(parser.data.roomId, parser.data.roomName);

        if (!GetConfigurationValue<boolean>('room.enter.info.enabled', true)) return;

        const data = parser.data;

        setRoomEnterInfo({
            roomId: data.roomId,
            roomName: data.roomName,
            ownerLine: data.showOwner
                ? `${localizeWithFallback('room.tool.room.owner.prefix', 'by')} ${data.ownerName}`
                : localizeWithFallback('room.tool.public.room', 'Public room'),
            tags: (data.tags ?? []).filter((tag) => !!tag).slice(0, ROOM_INFO_TAG_COUNT)
        });
    });

    // The entry card hides itself after a while; hovering keeps it open.
    useEffect(() => {
        if (!roomEnterInfo || isRoomInfoHovered) return;

        const delay = GetConfigurationValue<number>('room.enter.info.collapse.delay', 5000);

        roomInfoTimerRef.current = setTimeout(() => setRoomEnterInfo(null), Math.max(1000, delay));

        return () => clearTimeout(roomInfoTimerRef.current);
    }, [roomEnterInfo, isRoomInfoHovered]);

    const searchRoomTag = (tag: string) => {
        setRoomEnterInfo(null);
        CreateLinkEvent(`navigator/tag/${tag}`);
    };

    useEffect(() => {
        setRoomHistory(readRoomHistory());
    }, []);

    useEffect(() => {
        setHasLikedRoom(false);
        updateZoomScale();
    }, [roomSession?.roomId]);

    // Ctrl + mouse wheel over the room zooms through the same steps as the buttons.
    const onZoomLink = useEffectEvent((action: string) => handleToolClick(action));

    useEffect(() => {
        const linkTracker: ILinkEventTracker = {
            linkReceived: (url: string) => {
                const action = url.split('/')[1];

                if (action === 'zoom_in' || action === 'zoom_out') onZoomLink(action);
            },
            eventUrlPrefix: 'room-tools/'
        };

        AddLinkEventTracker(linkTracker);

        return () => RemoveLinkEventTracker(linkTracker);
    }, []);

    // The renderer can be zoomed from outside this toolbar (keyboard shortcuts,
    // other widgets), so resync the displayed level whenever the engine reports it.
    useOctaneEvent<RoomEngineEvent>(RoomEngineEvent.ROOM_ZOOMED, (event) => {
        if (!roomSession || event.roomId !== roomSession.roomId) return;

        updateZoomScale();
    });

    const tools = [
        ...(hasWiredAchievements ? [{ action: 'achievements', icon: 'icon-room-achievements', label: localizeWithFallback('room.achievements.button.text', 'Achievements') }] : []),
        { action: 'settings', icon: 'icon-cog', label: LocalizeText('room.settings.button.text') },
        { action: 'chat_history', icon: 'icon-chat-history', label: LocalizeText('room.chathistory.button.text') },
        ...(navigatorData.canRate || hasLikedRoom ? [{ action: 'like_room', icon: 'icon-like-room', label: LocalizeText('room.like.button.text'), disabled: hasLikedRoom }] : []),
        { action: 'toggle_room_link', icon: 'icon-room-link', label: LocalizeText('navigator.embed.caption') }
    ];

    const canZoomIn = getNextZoomScale(zoomScale, 1) !== getNearestZoomScale(zoomScale);
    const canZoomOut = getNextZoomScale(zoomScale, -1) !== getNearestZoomScale(zoomScale);

    return (
        <NativeTextTree>
            <div className={classNames('octane-room-tools-container', !isToolsOpen && 'is-collapsed')}>
                <BaseButton className="room-tools-collapse-toggle" type="button" onClick={() => setIsToolsOpen((prevValue) => !prevValue)}>
                    {isToolsOpen ? '‹' : '›'}
                </BaseButton>
                {isToolsOpen && (
                    <div className="octane-room-tools">
                        <div className="room-tools-zoom-row">
                            <span>{LocalizeText('room.zoom.text', ['zoom_level'], [getZoomText(zoomScale)])}</span>
                            <Hint content={LocalizeText('room.zoom.zoom_in.tooltip')}><BaseButton aria-label={LocalizeText('room.zoom.zoom_in.tooltip')} className="room-tools-zoom-button is-zoom-in" type="button" disabled={!canZoomIn} onClick={() => handleToolClick('zoom_in')} /></Hint>
                            <Hint content={LocalizeText('room.zoom.zoom_out.tooltip')}><BaseButton aria-label={LocalizeText('room.zoom.zoom_out.tooltip')} className="room-tools-zoom-button is-zoom-out" type="button" disabled={!canZoomOut} onClick={() => handleToolClick('zoom_out')} /></Hint>
                        </div>
                        {tools.map((tool) => (
                            <Hint key={tool.action} content={tool.label}><BaseButton aria-label={tool.label} type="button"
                                className={classNames('room-tool-row', tool.disabled && 'is-disabled')}
                                aria-disabled={tool.disabled || undefined}
                                onClick={() => !tool.disabled && handleToolClick(tool.action)}
                            >
                                <div className={classNames('octane-icon', tool.icon)} />
                                <span className="room-tool-label">{tool.label}</span>
                            </BaseButton></Hint>
                        ))}
                        {plugins.map((plugin) => (
                            <Hint key={plugin.name} content={plugin.label}><BaseButton aria-label={plugin.label} type="button"
                                className="room-tool-row"
                                onClick={() => plugin.onOpen()}
                            >
                                <div className={classNames('octane-icon', plugin.icon || 'icon-cog')} />
                                <span className="room-tool-label">{plugin.label}</span>
                            </BaseButton></Hint>
                        ))}
                        <div className="room-history-controls">
                            <Hint content={LocalizeText('room.history.button.back.tooltip')}><BaseButton aria-label={LocalizeText('room.history.button.back.tooltip')} type="button"
                                className={classNames('octane-icon', canGoBack ? 'cursor-pointer icon-room-history-back-enabled' : 'icon-room-history-back-disabled')}
                                aria-disabled={!canGoBack}
                                onClick={() => canGoBack && handleToolClick('room_history_back')}
                            /></Hint>
                            <Hint content={LocalizeText('room.history.button.tooltip')}><BaseButton aria-label={LocalizeText('room.history.button.tooltip')} type="button"
                                className={classNames('octane-icon', hasHistory ? 'cursor-pointer icon-room-history-enabled' : 'icon-room-history-disabled')}
                                aria-disabled={!hasHistory}
                                onClick={() => hasHistory && handleToolClick('room_history')}
                            /></Hint>
                            <Hint content={LocalizeText('room.history.button.forward.tooltip')}><BaseButton aria-label={LocalizeText('room.history.button.forward.tooltip')} type="button"
                                className={classNames('octane-icon', canGoNext ? 'cursor-pointer icon-room-history-next-enabled' : 'icon-room-history-next-disabled')}
                                aria-disabled={!canGoNext}
                                onClick={() => canGoNext && handleToolClick('room_history_next')}
                            /></Hint>
                        </div>
                    </div>
                )}
                <AnimatePresence>
                    {roomEnterInfo && roomEnterInfo.roomId === roomSession?.roomId && !isOpenHistory && (
                        <motion.div
                            key={roomEnterInfo.roomId}
                            initial={{ x: -320, opacity: 0 }}
                            animate={{ x: 0, opacity: 1 }}
                            exit={{ x: -320, opacity: 0 }}
                            transition={{ duration: 0.2 }}
                            className="octane-room-tools-info octane-room-tools-info--enter"
                            onClick={() => setRoomEnterInfo(null)}
                            onMouseEnter={() => setIsRoomInfoHovered(true)}
                            onMouseLeave={() => setIsRoomInfoHovered(false)}
                        >
                            <Text bold variant="white" className="block truncate text-[14px]">{roomEnterInfo.roomName}</Text>
                            <Text small className="block truncate text-[#999999]">{roomEnterInfo.ownerLine}</Text>
                            {roomEnterInfo.tags.length > 0 && (
                                <div className="flex gap-2 mt-1">
                                    {roomEnterInfo.tags.map((tag) => (
                                        <BaseButton
                                            key={tag}
                                            className="text-[12px] text-[#1b79ab] hover:text-[#47a8e6] bg-transparent border-0 p-0 cursor-pointer"
                                            type="button"
                                            onClick={(event) => {
                                                event.stopPropagation();
                                                searchRoomTag(tag);
                                            }}
                                        >
                                            #{trimRoomTag(tag)}
                                        </BaseButton>
                                    ))}
                                </div>
                            )}
                        </motion.div>
                    )}
                </AnimatePresence>
                <AnimatePresence>
                    {isOpenHistory && isToolsOpen && (
                        <motion.div
                            initial={{ x: -100 }}
                            animate={{ x: 0 }}
                            exit={{ x: -100 }}
                            transition={{ duration: 0.3 }}
                            className="octane-room-tools-history"
                        >
                            <div className="flex flex-col px-3 py-2 rounded octane-room-history">
                                {roomHistory.map((history) => (
                                    <Text component={BaseButton} type="button"
                                        key={history.roomId}
                                        bold={history.roomId === navigatorData.currentRoomId}
                                        variant="white"
                                        pointer
                                        className={classNames(
                                            'room-history-item',
                                            history.roomId === navigatorData.currentRoomId && 'room-history-item--current'
                                        )}
                                        onClick={() => TryVisitRoom(history.roomId)}
                                    >
                                        {history.roomName}
                                    </Text>
                                ))}
                            </div>
                        </motion.div>
                    )}
                </AnimatePresence>
            </div>
        </NativeTextTree>
    );
};
