/*
 * Copyright (C) Contributors to the Suwayomi project
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

import { useHotkeys as useHotKeysHook, useHotkeysContext } from 'react-hotkeys-hook';
import { useEffect } from 'react';
import { useTheme } from '@mui/material/styles';
import { HOTKEY_SCOPES } from '@/modules/hotkeys/Hotkeys.constants.ts';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { IReaderSettings, ReaderHotkey } from '@/modules/reader/types/Reader.types.ts';
import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx';
import { getNextRotationValue } from '@/modules/core/utils/ValueRotationButton.utils.ts';
import {
    AUTO_SCROLL_SPEED,
    CONTINUOUS_READING_MODE_TO_SCROLL_DIRECTION,
    READER_PAGE_SCALE_MODE_VALUES,
    ReaderScrollAmount,
    READING_DIRECTION_VALUES,
    READING_MODE_VALUES,
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
import { HotkeyScope } from '@/modules/hotkeys/Hotkeys.types.ts';
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
import { ScrollOffset } from '@/modules/core/Core.types.ts';
import { getOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts';
import { FALLBACK_MANGA } from '@/modules/manga/Manga.constants.ts';
import { useReaderAutoScrollContext } from '@/modules/reader/contexts/ReaderAutoScrollContext.tsx';
import { useReaderTapZoneContext } from '@/modules/reader/contexts/ReaderTapZoneContext.tsx';

const useHotkeys = (...args: Parameters<typeof useHotKeysHook>): ReturnType<typeof useHotKeysHook> => {
    const [keys, callback, options, dependencies] = args;
    return useHotKeysHook(keys, callback, { ...options, ...HOTKEY_SCOPES.reader }, dependencies);
};

const updateSettingCycleThrough = <Setting extends keyof IReaderSettings>(
    updateSetting: ReturnType<typeof ReaderService.useCreateUpdateSetting>,
    deleteSetting: ReturnType<typeof ReaderService.useCreateDeleteSetting>,
    setting: Setting,
    value: IReaderSettings[Setting],
    values: IReaderSettings[Setting][],
    isDefault: boolean,
    isDefaultable: boolean,
) => {
    if (isDefault) {
        updateSetting(setting, values[0]);
        return;
    }

    const nextValue = getNextRotationValue(values.indexOf(value), values, isDefaultable);

    const isDefaultNextValue = nextValue === undefined;
    if (isDefaultNextValue) {
        deleteSetting(setting);
        return;
    }

    updateSetting(setting, nextValue);
};

export const ReaderHotkeys = ({
    scrollElementRef,
}: {
    scrollElementRef: React.MutableRefObject<HTMLElement | null>;
}) => {
    const { direction: themeDirection } = useTheme();
    const readerThemeDirection = ReaderService.useGetThemeDirection();
    const { enableScope, disableScope } = useHotkeysContext();
    const { manga } = useReaderStateMangaContext();
    const { isVisible, setIsVisible: setIsOverlayVisible } = useReaderOverlayContext();
    const {
        hotkeys,
        pageScaleMode,
        shouldStretchPage,
        shouldOffsetDoubleSpreads,
        readingMode,
        readingDirection,
        autoScroll,
    } = ReaderService.useSettings();
    const automaticScrolling = useReaderAutoScrollContext();
    const { setShowPreview } = useReaderTapZoneContext();

    const openChapter = ReaderControls.useOpenChapter();
    const openPage = ReaderControls.useOpenPage();

    const updateSetting = ReaderService.useCreateUpdateSetting(manga ?? FALLBACK_MANGA);
    const deleteSetting = ReaderService.useCreateDeleteSetting(manga ?? FALLBACK_MANGA);

    useHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => openPage('previous'), [openPage]);
    useHotkeys(hotkeys[ReaderHotkey.NEXT_PAGE], () => openPage('next'), [openPage]);
    useHotkeys(
        hotkeys[ReaderHotkey.SCROLL_BACKWARD],
        () =>
            scrollElementRef.current &&
            ReaderControls.scroll(
                ScrollOffset.BACKWARD,
                CONTINUOUS_READING_MODE_TO_SCROLL_DIRECTION[readingMode.value],
                readingMode.value,
                readingDirection.value,
                themeDirection,
                scrollElementRef.current,
                openChapter,
                setIsOverlayVisible,
                setShowPreview,
                ReaderScrollAmount.SMALL,
            ),
        { preventDefault: true },
        [readingMode.value, readingDirection.value, themeDirection, openChapter],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.SCROLL_FORWARD],
        () =>
            scrollElementRef.current &&
            ReaderControls.scroll(
                ScrollOffset.FORWARD,
                CONTINUOUS_READING_MODE_TO_SCROLL_DIRECTION[readingMode.value],
                readingMode.value,
                readingDirection.value,
                themeDirection,
                scrollElementRef.current,
                openChapter,
                setIsOverlayVisible,
                setShowPreview,
                ReaderScrollAmount.SMALL,
            ),
        { preventDefault: true },
        [readingMode.value, readingDirection.value, themeDirection, openChapter],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.PREVIOUS_CHAPTER],
        () => openChapter(getOptionForDirection('previous', 'next', readerThemeDirection)),
        [openChapter, readerThemeDirection],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.NEXT_CHAPTER],
        () => openChapter(getOptionForDirection('next', 'previous', readerThemeDirection)),
        [openChapter, readerThemeDirection],
    );
    useHotkeys(hotkeys[ReaderHotkey.TOGGLE_MENU], () => setIsOverlayVisible(!isVisible), [isVisible]);
    useHotkeys(
        hotkeys[ReaderHotkey.CYCLE_SCALE_TYPE],
        () => {
            updateSettingCycleThrough(
                updateSetting,
                deleteSetting,
                'pageScaleMode',
                pageScaleMode.value,
                READER_PAGE_SCALE_MODE_VALUES,
                pageScaleMode.isDefault,
                true,
            );
        },
        [updateSetting, deleteSetting, pageScaleMode.value, pageScaleMode.isDefault],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.STRETCH_IMAGE],
        () => updateSetting('shouldStretchPage', !shouldStretchPage.value),
        [updateSetting, shouldStretchPage.value],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.OFFSET_SPREAD_PAGES],
        () => updateSetting('shouldOffsetDoubleSpreads', !shouldOffsetDoubleSpreads.value),
        [updateSetting, shouldOffsetDoubleSpreads.value],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.CYCLE_READING_MODE],
        () => {
            updateSettingCycleThrough(
                updateSetting,
                deleteSetting,
                'readingMode',
                readingMode.value,
                READING_MODE_VALUES,
                readingMode.isDefault,
                true,
            );
        },
        [updateSetting, deleteSetting, readingMode.value, readingMode.isDefault],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.CYCLE_READING_DIRECTION],
        () => {
            updateSettingCycleThrough(
                updateSetting,
                deleteSetting,
                'readingDirection',
                readingDirection.value,
                READING_DIRECTION_VALUES,
                readingDirection.isDefault,
                true,
            );
        },
        [updateSetting, deleteSetting, readingDirection.value, readingDirection.isDefault],
    );
    useHotkeys(hotkeys[ReaderHotkey.TOGGLE_AUTO_SCROLL], automaticScrolling.toggleActive, { preventDefault: true }, [
        automaticScrolling.toggleActive,
    ]);
    useHotkeys(
        hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_INCREASE],
        () =>
            updateSetting('autoScroll', {
                ...autoScroll,
                value: Math.min(AUTO_SCROLL_SPEED.max, autoScroll.value + AUTO_SCROLL_SPEED.step),
            }),
        [updateSetting, autoScroll.value],
    );
    useHotkeys(
        hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE],
        () =>
            updateSetting('autoScroll', {
                ...autoScroll,
                value: Math.max(AUTO_SCROLL_SPEED.min, autoScroll.value - AUTO_SCROLL_SPEED.step),
            }),
        [updateSetting, autoScroll.value],
    );

    useEffect(() => {
        enableScope(HotkeyScope.READER);

        return () => disableScope(HotkeyScope.READER);
    }, []);

    return null;
};
