import React, { type ReactElement, type Ref, useRef, useImperativeHandle } from 'react';
import { AccessibilityInfo, findNodeHandle, type LayoutChangeEvent } from 'react-native';
import { useBackHandler } from '@react-native-community/hooks';
import { Q } from '@nozbe/watermelondb';
import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated';

import { useRoomContext } from '../../views/RoomView/context';
import { Autocomplete } from './components';
import { MIN_HEIGHT } from './constants';
import {
	MessageInnerContext,
	useAlsoSendThreadToChannel,
	useComposerAttachments,
	useMessageComposerApi,
	useRecordingAudio
} from './context';
import { type IComposerInput, type IMessageComposerRef } from './interfaces';
import { EventTypes } from '../EmojiPicker/interfaces';
import { type IEmoji } from '../../definitions';
import database from '../../lib/database';
import { sanitizeLikeString } from '../../lib/database/utils';
import { generateTriggerId } from '../../lib/methods/actions';
import { runSlashCommand } from '../../lib/services/restApi';
import log from '../../lib/methods/helpers/log';
import { prepareQuoteMessage, insertEmojiAtCursor } from './helpers';
import useShortnameToUnicode from '../../lib/hooks/useShortnameToUnicode';
import { useCloseKeyboardWhenOrientationChanges } from './hooks/useCloseKeyboardWhenOrientationChanges';
import { useEmojiKeyboard } from './hooks/useEmojiKeyboard';
import EmojiPicker from '../EmojiPicker';
import { MessageComposerContent } from './components/MessageComposerContent';
import { useTheme } from '../../theme';
import { useAppSelector } from '../../lib/hooks/useAppSelector';
import { getUserSelector } from '../../selectors/login';
import { sendAttachments } from '../../lib/methods/sendFileMessage/sendAttachments';
import { useAltTextSupported } from '../../lib/hooks/useAltTextSupported';

export const MessageComposer = ({
	forwardedRef,
	children
}: {
	forwardedRef: Ref<IMessageComposerRef>;
	children?: ReactElement | null;
}): ReactElement | null => {
	'use memo';

	const composerInputRef = useRef(null);
	const composerInputComponentRef = useRef<IComposerInput>({
		getTextAndClear: () => '',
		getText: () => '',
		getSelection: () => ({ start: 0, end: 0 }),
		setInput: () => {},
		onAutocompleteItemSelected: () => {},
		focus: () => {}
	});
	const contentHeight = useSharedValue(MIN_HEIGHT);
	useCloseKeyboardWhenOrientationChanges();
	const { rid, tmid, action, selectedMessages, sharing, editRequest, onSendMessage, setQuotesAndText } = useRoomContext();
	const alsoSendThreadToChannel = useAlsoSendThreadToChannel();
	const { showEmojiKeyboard, showEmojiSearchbar, openEmojiSearchbar, resetKeyboard, keyboardHeight } = useEmojiKeyboard();
	const { setAlsoSendThreadToChannel, setAutocompleteParams, clearAttachments } = useMessageComposerApi();
	const recordingAudio = useRecordingAudio();
	const { formatShortnameToUnicode } = useShortnameToUnicode();
	const { colors } = useTheme();
	const user = useAppSelector(state => getUserSelector(state));
	const server = useAppSelector(state => state.server.server);
	const altTextSupported = useAltTextSupported();
	const attachments = useComposerAttachments();

	useBackHandler(() => {
		if (showEmojiSearchbar) {
			resetKeyboard();
			return true;
		}
		return false;
	});

	const closeEmojiKeyboardAndAction = (action?: Function, params?: any) => {
		resetKeyboard();
		action && action(params);
	};

	useImperativeHandle(forwardedRef, () => ({
		closeEmojiKeyboardAndAction,
		getText: () => composerInputComponentRef.current.getText(),
		setInput: (...args: Parameters<IMessageComposerRef['setInput']>) => composerInputComponentRef.current.setInput(...args),
		focus: () => composerInputComponentRef.current.focus()
	}));

	const handleLayout = (event: LayoutChangeEvent) => {
		const { height } = event.nativeEvent.layout;
		contentHeight.value = height;
	};

	const handleSendMessage = async () => {
		if (!rid) return;

		if (alsoSendThreadToChannel) {
			setAlsoSendThreadToChannel(false);
		}

		// Hide autocomplete
		setAutocompleteParams({ text: '', type: null, params: '' });

		if (sharing) {
			onSendMessage?.();
			return;
		}

		const textFromInput = composerInputComponentRef.current.getTextAndClear();

		if (action === 'edit') {
			const updatedAttachments = attachments.length
				? attachments.map(({ description, altText, fileId, filename }) =>
						altTextSupported ? { description: altText || '', fileId, filename } : { description: description || '' }
				  )
				: undefined;
			editRequest?.({ id: selectedMessages[0], msg: textFromInput, rid, attachments: updatedAttachments });
			clearAttachments();
			return;
		}

		if (attachments.length) {
			let quotedMessage: string | undefined;

			if (action === 'quote') {
				quotedMessage = await prepareQuoteMessage(textFromInput, selectedMessages);
			}

			try {
				await sendAttachments({
					attachments,
					rid,
					tmid,
					server,
					user: { id: user.id, token: user.token },
					altTextSupported,
					getMsg: ({ description }, index) => (index === 0 ? description || quotedMessage || textFromInput : description)
				});
				clearAttachments();
				setQuotesAndText?.('', []);
				return;
			} catch (e) {
				log(e);
				composerInputComponentRef.current.setInput(textFromInput);
				return;
			}
		}

		if (action === 'quote') {
			const quoteMessage = await prepareQuoteMessage(textFromInput, selectedMessages);
			onSendMessage?.(quoteMessage);
			return;
		}

		// Slash command
		if (textFromInput[0] === '/') {
			const db = database.active;
			const commandsCollection = db.get('slash_commands');
			const command = textFromInput.replace(/ .*/, '').slice(1);
			const likeString = sanitizeLikeString(command);
			const slashCommand = await commandsCollection.query(Q.where('id', Q.like(`${likeString}%`))).fetch();
			if (slashCommand.length > 0) {
				try {
					const messageWithoutCommand = textFromInput.replace(/([^\s]+)/, '').trim();
					const [{ appId }] = slashCommand;
					const triggerId = generateTriggerId(appId);
					await runSlashCommand(command, rid, messageWithoutCommand, triggerId, tmid);
				} catch (e) {
					log(e);
				}
				return;
			}
		}

		// Text message
		onSendMessage?.(textFromInput, alsoSendThreadToChannel);
	};

	const onKeyboardItemSelected = (eventType: EventTypes, emoji?: IEmoji) => {
		const text = composerInputComponentRef.current.getText();
		let newText = '';
		// if input has an active cursor
		const { start, end } = composerInputComponentRef.current.getSelection();
		const cursor = Math.max(start, end);
		let newCursor;

		switch (eventType) {
			case EventTypes.BACKSPACE_PRESSED:
				const emojiRegex = /\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]/;
				let charsToRemove = 1;
				const lastEmoji = text.substr(cursor > 0 ? cursor - 2 : text.length - 2, cursor > 0 ? cursor : text.length);
				// Check if last character is an emoji
				if (emojiRegex.test(lastEmoji)) charsToRemove = 2;
				newText =
					text.substr(0, (cursor > 0 ? cursor : text.length) - charsToRemove) + text.substr(cursor > 0 ? cursor : text.length);
				newCursor = cursor - charsToRemove;
				composerInputComponentRef.current.setInput(newText, { start: newCursor, end: newCursor });
				break;
			case EventTypes.EMOJI_PRESSED:
				let emojiText = '';
				if (typeof emoji === 'string') {
					emojiText = formatShortnameToUnicode(`:${emoji}:`);
				} else if (emoji?.name) {
					emojiText = `:${emoji.name}:`;
				}
				const { updatedCursor, updatedText } = insertEmojiAtCursor(text, emojiText, cursor);
				composerInputComponentRef.current.setInput(updatedText, { start: updatedCursor, end: updatedCursor });
				break;
			case EventTypes.SEARCH_PRESSED:
				openEmojiSearchbar();
				break;
			default:
			// Do nothing
		}
	};

	const onEmojiSelected = (emoji: IEmoji) => {
		onKeyboardItemSelected(EventTypes.EMOJI_PRESSED, emoji);
	};

	const focusComposerInput = () => composerInputComponentRef.current?.focus();

	const accessibilityFocusOnInput = () => {
		const node = findNodeHandle(composerInputRef.current);
		if (node) {
			AccessibilityInfo.setAccessibilityFocus(node);
		}
	};

	const emojiKeyboardStyle = useAnimatedStyle(() => ({
		height: keyboardHeight.value
	}));

	const autocompleteStyle = useAnimatedStyle(() => ({
		bottom: keyboardHeight.value + contentHeight.value - 4
	}));

	return (
		<MessageInnerContext.Provider
			value={{
				sendMessage: handleSendMessage,
				onEmojiSelected,
				closeEmojiKeyboardAndAction,
				focus: focusComposerInput
			}}>
			<MessageComposerContent
				recordingAudio={recordingAudio}
				action={action}
				composerInputComponentRef={composerInputComponentRef}
				composerInputRef={composerInputRef}
				onLayout={handleLayout}>
				{children}
			</MessageComposerContent>
			<Animated.View style={[emojiKeyboardStyle, { backgroundColor: colors.surfaceLight }]}>
				{showEmojiKeyboard && !showEmojiSearchbar ? <EmojiPicker onItemClicked={onKeyboardItemSelected} isEmojiKeyboard /> : null}
			</Animated.View>
			<Autocomplete
				onPress={item => composerInputComponentRef.current.onAutocompleteItemSelected(item)}
				style={autocompleteStyle}
				accessibilityFocusOnInput={accessibilityFocusOnInput}
			/>
		</MessageInnerContext.Provider>
	);
};
