import reducer from '@joplin/lib/reducer';
import { AppState } from './types';
import appDefaultState, { DEFAULT_ROUTE } from './appDefaultState';
import fastDeepEqual = require('fast-deep-equal');
import Logger from '@joplin/utils/Logger';

const logger = Logger.create('appReducer');

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const navHistory: any[] = [];

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function historyCanGoBackTo(route: any) {
	if (route.routeName === 'Folder') return false;

	// This is an intermediate screen that acts more like a modal -- it should be skipped in the
	// navigation history.
	if (route.routeName === 'DocumentScanner') return false;

	// There's no point going back to these screens in general and, at least in OneDrive case,
	// it can be buggy to do so, due to incorrectly relying on global state (reg.syncTarget...)
	if (route.routeName === 'OneDriveLogin') return false;
	if (route.routeName === 'DropboxLogin') return false;

	return true;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Assigning types to these variables would be too big of a refactoring
function removeAdjacentNoteDuplicates(items: any[]) {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Assigning types to these variables would be too big of a refactoring
	return items.filter((item: any, idx: number) => (idx >= 1) ? !(item.routeName === 'Note' && items[idx - 1].routeName === 'Note' && items[idx - 1].noteId === item.noteId) : true);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Assigning types to these variables would be too big of a refactoring
function removeAdjacentFolderDuplicates(items: any[]) {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Assigning types to these variables would be too big of a refactoring
	return items.filter((item: any, idx: number) => (idx >= 1) ? !(item.routeName === 'Notes' && items[idx - 1].routeName === 'Notes' && items[idx - 1].folderId === item.folderId) : true);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Assigning types to these variables would be too big of a refactoring
function removeLatestFolderIfSelected(items: any[], route: any) {
	if (items.length && route.routeName === 'Notes' && items[items.length - 1].folderId === route.folderId) {
		items.splice(items.length - 1, 1);
	}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const appReducer = (state = appDefaultState, action: any) => {
	let newState = state;
	let historyGoingBack = false;

	try {
		switch (action.type) {

		case 'NAV_BACK':
		case 'NAV_GO':

			if (action.type === 'NAV_BACK') {
				if (!navHistory.length) break;

				const newAction = navHistory.pop();
				action = newAction ? newAction : navHistory.pop();

				historyGoingBack = true;
			}

			{
				const currentRoute = state.route;

				if (!historyGoingBack && historyCanGoBackTo(currentRoute)) {
					const previousRoute = navHistory.length && navHistory[navHistory.length - 1];
					const isDifferentRoute = !previousRoute || !fastDeepEqual(navHistory[navHistory.length - 1], currentRoute);

					// Avoid multiple consecutive duplicate screens in the navigation history -- these can make
					// pressing "back" seem to have no effect.
					if (currentRoute.isDeleted) {
						// Do not add the item to the history, and remove the last item in the history if that is now the selected item
						removeLatestFolderIfSelected(navHistory, action);
						// Push DEFAULT_ROUTE so there's always a valid back target after deletion
						if (!navHistory.length) {
							navHistory.push(DEFAULT_ROUTE);
						}
					} else if (isDifferentRoute) {
						navHistory.push(currentRoute);
					}
				}

				if (action.clearHistory) {
					navHistory.splice(0, navHistory.length);
				}

				newState = { ...state };

				newState.selectedNoteHash = '';

				if (currentRoute.routeName === 'Search' && action.routeName === 'Notes') {
					// Force a reload of the note list
					newState.notesSource = '';
				}

				if (action.routeName === 'Search') {
					newState.notesParentType = 'Search';
				}

				if ('noteId' in action) {
					newState.selectedNoteIds = action.noteId ? [action.noteId] : [];
				}

				if ('folderId' in action) {
					newState.selectedFolderId = action.folderId;
					newState.selectedFolderIds = [action.folderId];
					newState.notesParentType = 'Folder';
				}

				if ('tagId' in action) {
					newState.selectedTagId = action.tagId;
					newState.selectedTagIds = [action.tagId];
					newState.notesParentType = 'Tag';
				}

				if ('smartFilterId' in action) {
					newState.smartFilterId = action.smartFilterId;
					newState.selectedSmartFilterId = action.smartFilterId;
					newState.notesParentType = 'SmartFilter';
				}

				if ('itemType' in action) {
					newState.selectedItemType = action.itemType;
				}

				if ('noteHash' in action) {
					newState.selectedNoteHash = action.noteHash;
				}

				if ('sharedData' in action) {
					newState.sharedData = action.sharedData;
				} else {
					newState.sharedData = null;
				}

				newState.route = action;
				newState.historyCanGoBack = !!navHistory.length;

				logger.debug('Navigated to route:', newState.route?.routeName, 'with notesParentType:', newState.notesParentType);
			}
			break;

		case 'FOLDER_DELETE':

			{
				let newNavHistoryForFolder = navHistory.filter(route => !(route.routeName === 'Notes' && route.folderId === action.id));
				newNavHistoryForFolder = removeAdjacentFolderDuplicates(newNavHistoryForFolder);
				navHistory.splice(0, navHistory.length, ...newNavHistoryForFolder);

				// Prevent the deleted folder from being added to the navigation history again when navigating forward, where the selected folder was deleted
				newState = {
					...state,
					route: {
						...state.route,
						isDeleted: true,
					},
				};
			}
			break;

		case 'NOTE_DELETE':

			{
				let newNavHistory = navHistory.filter(route => !(route.routeName === 'Note' && route.noteId === action.id));
				newNavHistory = removeAdjacentNoteDuplicates(newNavHistory);

				// Fix the case where after deletion, the currently selected folder is also the latest in history
				// Notes are not relevant for this scenario, because both note and folder deletion redirects to a folder rather than a note on mobile
				removeLatestFolderIfSelected(newNavHistory, state.route);

				navHistory.splice(0, navHistory.length, ...newNavHistory);
			}
			break;

		case 'SIDE_MENU_TOGGLE':

			newState = { ...state };
			newState.showSideMenu = !newState.showSideMenu;
			break;

		case 'SIDE_MENU_OPEN':

			newState = { ...state };
			newState.showSideMenu = true;
			break;

		case 'SIDE_MENU_CLOSE':

			newState = { ...state };
			newState.showSideMenu = false;
			break;

		case 'SET_PLUGIN_PANELS_DIALOG_VISIBLE':
			newState = { ...state };
			newState.showPanelsDialog = action.visible;
			break;

		case 'NOTE_SELECTION_TOGGLE':

			{
				newState = { ...state };

				const noteId = action.id;
				const newSelectedNoteIds = state.selectedNoteIds.slice();
				const existingIndex = state.selectedNoteIds.indexOf(noteId);

				if (existingIndex >= 0) {
					newSelectedNoteIds.splice(existingIndex, 1);
				} else {
					newSelectedNoteIds.push(noteId);
				}

				newState.selectedNoteIds = newSelectedNoteIds;
				newState.noteSelectionEnabled = !!newSelectedNoteIds.length;
			}
			break;

		case 'NOTE_SELECTION_START':

			if (!state.noteSelectionEnabled) {
				newState = { ...state };
				newState.noteSelectionEnabled = true;
				newState.selectedNoteIds = [action.id];
			}
			break;

		case 'NOTE_SELECTION_END':

			newState = { ...state };
			newState.noteSelectionEnabled = false;
			newState.selectedNoteIds = [];
			break;

		case 'NOTE_SIDE_MENU_OPTIONS_SET':

			newState = { ...state };
			newState.noteSideMenuOptions = action.options;
			break;

		case 'SET_SIDE_MENU_TOUCH_GESTURES_DISABLED':
			newState = { ...state };
			newState.disableSideMenuGestures = action.disableSideMenuGestures;
			break;

		case 'MOBILE_DATA_WARNING_UPDATE':

			newState = { ...state };
			newState.isOnMobileData = action.isOnMobileData;
			break;

		case 'KEYBOARD_VISIBLE_CHANGE':
			newState = { ...state, keyboardVisible: action.visible };
			break;

		case 'NOTE_EDITOR_VISIBLE_CHANGE':
			newState = { ...state, noteEditorVisible: action.visible };
			break;

		case 'SYNC_WIZARD_VISIBLE_CHANGE':
			newState = { ...state, syncWizardVisible: action.visible };
			break;

		case 'NOTE_VISIBLE_PANES_SET':
			newState = {
				...state,
				noteVisiblePanes: Array.isArray(action.panes) && action.panes.length ? action.panes : ['viewer'],
			};
			break;
		}
	} catch (error) {
		error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
		throw error;
	}

	return reducer(newState, action) as AppState;
};

export default appReducer;
