import React from 'react';
import { Keyboard, ViewStyle } from 'react-native';
import { Subscription } from 'rxjs';

import Message from './Message';
import MessageContext from './Context';
import debounce from '../../utils/debounce';
import { getMessageTranslation } from './utils';
import { TSupportedThemes, withTheme } from '../../theme';
import openLink from '../../utils/openLink';
import { IAttachment, TAnyMessageModel, TGetCustomEmoji } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView';
import { E2E_MESSAGE_TYPE, E2E_STATUS, messagesStatus } from '../../lib/constants';

interface IMessageContainerProps {
	item: TAnyMessageModel;
	user: {
		id: string;
		username: string;
		token: string;
	};
	msg?: string;
	rid: string;
	timeFormat?: string;
	style?: ViewStyle;
	archived?: boolean;
	broadcast?: boolean;
	previousItem?: TAnyMessageModel;
	baseUrl: string;
	Message_GroupingPeriod?: number;
	isReadReceiptEnabled?: boolean;
	isThreadRoom: boolean;
	isSystemMessage?: boolean;
	useRealName?: boolean;
	autoTranslateRoom?: boolean;
	autoTranslateLanguage?: string;
	status?: number;
	isIgnored?: boolean;
	highlighted?: boolean;
	getCustomEmoji: TGetCustomEmoji;
	onLongPress?: (item: TAnyMessageModel) => void;
	onReactionPress?: (emoji: string, id: string) => void;
	onEncryptedPress?: () => void;
	onDiscussionPress?: (item: TAnyMessageModel) => void;
	onThreadPress?: (item: TAnyMessageModel) => void;
	errorActionsShow?: (item: TAnyMessageModel) => void;
	replyBroadcast?: (item: TAnyMessageModel) => void;
	reactionInit?: (item: TAnyMessageModel) => void;
	fetchThreadName?: (tmid: string, id: string) => Promise<string | undefined>;
	showAttachment: (file: IAttachment) => void;
	onReactionLongPress?: (item: TAnyMessageModel) => void;
	navToRoomInfo: (navParam: IRoomInfoParam) => void;
	callJitsi?: () => void;
	blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
	onAnswerButtonPress?: (message: string, tmid?: string, tshow?: boolean) => void;
	threadBadgeColor?: string;
	toggleFollowThread?: (isFollowingThread: boolean, tmid?: string) => Promise<void>;
	jumpToMessage?: (link: string) => void;
	onPress?: () => void;
	theme: TSupportedThemes;
}

interface IMessageContainerState {
	isManualUnignored: boolean;
}

class MessageContainer extends React.Component<IMessageContainerProps, IMessageContainerState> {
	static defaultProps = {
		getCustomEmoji: () => null,
		onLongPress: () => {},
		callJitsi: () => {},
		blockAction: () => {},
		archived: false,
		broadcast: false,
		isIgnored: false,
		theme: 'light' as TSupportedThemes
	};

	state = { isManualUnignored: false };

	private subscription?: Subscription;

	componentDidMount() {
		const { item } = this.props;
		if (item && item.observe) {
			const observable = item.observe();
			this.subscription = observable.subscribe(() => {
				this.forceUpdate();
			});
		}
	}

	shouldComponentUpdate(nextProps: IMessageContainerProps, nextState: IMessageContainerState) {
		const { isManualUnignored } = this.state;
		const { threadBadgeColor, isIgnored, highlighted } = this.props;
		if (nextProps.highlighted !== highlighted) {
			return true;
		}
		if (nextProps.threadBadgeColor !== threadBadgeColor) {
			return true;
		}
		if (nextProps.isIgnored !== isIgnored) {
			return true;
		}
		if (nextState.isManualUnignored !== isManualUnignored) {
			return true;
		}
		return false;
	}

	componentWillUnmount() {
		if (this.subscription && this.subscription.unsubscribe) {
			this.subscription.unsubscribe();
		}
	}

	onPress = debounce(
		() => {
			const { onPress } = this.props;
			if (this.isIgnored) {
				return this.onIgnoredMessagePress();
			}

			if (onPress) {
				return onPress();
			}

			const { item, isThreadRoom } = this.props;
			Keyboard.dismiss();

			if ((item.tlm || item.tmid) && !isThreadRoom) {
				this.onThreadPress();
			}

			const { onDiscussionPress } = this.props;

			if (item.dlm && onDiscussionPress) {
				onDiscussionPress(item);
			}
		},
		300,
		true
	);

	onLongPress = () => {
		const { archived, onLongPress, item } = this.props;
		if (this.isInfo || this.hasError || this.isEncrypted || archived) {
			return;
		}
		if (onLongPress) {
			onLongPress(item);
		}
	};

	onErrorPress = () => {
		const { errorActionsShow, item } = this.props;
		if (errorActionsShow) {
			errorActionsShow(item);
		}
	};

	onReactionPress = (emoji: string) => {
		const { onReactionPress, item } = this.props;
		if (onReactionPress) {
			onReactionPress(emoji, item.id);
		}
	};

	onReactionLongPress = () => {
		const { onReactionLongPress, item } = this.props;
		if (onReactionLongPress) {
			onReactionLongPress(item);
		}
	};

	onEncryptedPress = () => {
		const { onEncryptedPress } = this.props;
		if (onEncryptedPress) {
			onEncryptedPress();
		}
	};

	onDiscussionPress = () => {
		const { onDiscussionPress, item } = this.props;
		if (onDiscussionPress) {
			onDiscussionPress(item);
		}
	};

	onThreadPress = () => {
		const { onThreadPress, item } = this.props;
		if (onThreadPress) {
			onThreadPress(item);
		}
	};

	onAnswerButtonPress = (msg: string) => {
		const { onAnswerButtonPress } = this.props;
		if (onAnswerButtonPress) {
			onAnswerButtonPress(msg, undefined, false);
		}
	};

	onIgnoredMessagePress = () => {
		this.setState({ isManualUnignored: true });
	};

	get isHeader(): boolean {
		const { item, previousItem, broadcast, Message_GroupingPeriod } = this.props;
		if (this.hasError || (previousItem && previousItem.status === messagesStatus.ERROR)) {
			return true;
		}
		try {
			if (
				previousItem &&
				// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
				previousItem.ts.toDateString() === item.ts.toDateString() &&
				previousItem.u.username === item.u.username &&
				!(previousItem.groupable === false || item.groupable === false || broadcast === true) &&
				// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
				item.ts - previousItem.ts < Message_GroupingPeriod * 1000 &&
				previousItem.tmid === item.tmid
			) {
				return false;
			}
			return true;
		} catch (error) {
			return true;
		}
	}

	get isThreadReply(): boolean {
		const { item, previousItem, isThreadRoom } = this.props;
		if (isThreadRoom) {
			return false;
		}
		if (previousItem && item.tmid && previousItem.tmid !== item.tmid && previousItem.id !== item.tmid) {
			return true;
		}
		return false;
	}

	get isThreadSequential(): boolean {
		const { item, isThreadRoom } = this.props;
		if (isThreadRoom) {
			return false;
		}
		return !!item.tmid;
	}

	get isEncrypted(): boolean {
		const { item } = this.props;
		const { t, e2e } = item;
		return t === E2E_MESSAGE_TYPE && e2e !== E2E_STATUS.DONE;
	}

	get isInfo(): string | boolean {
		const { item } = this.props;
		if (['e2e', 'discussion-created'].includes(item.t)) {
			return false;
		}
		return item.t;
	}

	get isTemp(): boolean {
		const { item } = this.props;
		return item.status === messagesStatus.TEMP || item.status === messagesStatus.ERROR;
	}

	get isIgnored(): boolean {
		const { isManualUnignored } = this.state;
		const { isIgnored } = this.props;
		if (isManualUnignored) {
			return false;
		}
		return isIgnored ?? false;
	}

	get hasError(): boolean {
		const { item } = this.props;
		return item.status === messagesStatus.ERROR;
	}

	reactionInit = () => {
		const { reactionInit, item } = this.props;
		if (reactionInit) {
			reactionInit(item);
		}
	};

	replyBroadcast = () => {
		const { replyBroadcast, item } = this.props;
		if (replyBroadcast) {
			replyBroadcast(item);
		}
	};

	onLinkPress = (link: string): void => {
		const { item, jumpToMessage, theme } = this.props;
		const isMessageLink = item?.attachments?.findIndex((att: IAttachment) => att?.message_link === link) !== -1;
		if (isMessageLink && jumpToMessage) {
			return jumpToMessage(link);
		}
		openLink(link, theme);
	};

	render() {
		const {
			item,
			user,
			style,
			archived,
			baseUrl,
			useRealName,
			broadcast,
			fetchThreadName,
			showAttachment,
			timeFormat,
			isReadReceiptEnabled,
			autoTranslateRoom,
			autoTranslateLanguage,
			navToRoomInfo,
			getCustomEmoji,
			isThreadRoom,
			callJitsi,
			blockAction,
			rid,
			threadBadgeColor,
			toggleFollowThread,
			jumpToMessage,
			highlighted
		} = this.props;
		const {
			id,
			msg,
			ts,
			attachments,
			urls,
			reactions,
			t,
			avatar,
			emoji,
			u,
			alias,
			editedBy,
			role,
			drid,
			dcount,
			dlm,
			tmid,
			tcount,
			tlm,
			tmsg,
			mentions,
			channels,
			unread,
			blocks,
			autoTranslate: autoTranslateMessage,
			replies,
			md,
			comment
		} = item;

		let message = msg;
		// "autoTranslateRoom" and "autoTranslateLanguage" are properties from the subscription
		// "autoTranslateMessage" is a toggle between "View Original" and "Translate" state
		if (autoTranslateRoom && autoTranslateMessage && autoTranslateLanguage) {
			message = getMessageTranslation(item, autoTranslateLanguage) || message;
		}

		return (
			<MessageContext.Provider
				value={{
					user,
					baseUrl,
					onPress: this.onPress,
					onLongPress: this.onLongPress,
					reactionInit: this.reactionInit,
					onErrorPress: this.onErrorPress,
					replyBroadcast: this.replyBroadcast,
					onReactionPress: this.onReactionPress,
					onEncryptedPress: this.onEncryptedPress,
					onDiscussionPress: this.onDiscussionPress,
					onReactionLongPress: this.onReactionLongPress,
					onLinkPress: this.onLinkPress,
					onAnswerButtonPress: this.onAnswerButtonPress,
					jumpToMessage,
					threadBadgeColor,
					toggleFollowThread,
					replies
				}}>
				{/* @ts-ignore*/}
				<Message
					id={id}
					msg={message}
					md={md}
					rid={rid}
					author={u}
					ts={ts}
					type={t}
					attachments={attachments}
					blocks={blocks}
					urls={urls}
					reactions={reactions}
					alias={alias}
					avatar={avatar}
					emoji={emoji}
					timeFormat={timeFormat}
					style={style}
					archived={archived}
					broadcast={broadcast}
					useRealName={useRealName}
					isReadReceiptEnabled={isReadReceiptEnabled}
					unread={unread}
					role={role}
					drid={drid}
					dcount={dcount}
					dlm={dlm}
					tmid={tmid}
					tcount={tcount}
					tlm={tlm}
					tmsg={tmsg}
					fetchThreadName={fetchThreadName}
					mentions={mentions}
					channels={channels}
					isIgnored={this.isIgnored}
					isEdited={(editedBy && !!editedBy.username) ?? false}
					isHeader={this.isHeader}
					isThreadReply={this.isThreadReply}
					isThreadSequential={this.isThreadSequential}
					isThreadRoom={isThreadRoom}
					isInfo={this.isInfo}
					isTemp={this.isTemp}
					isEncrypted={this.isEncrypted}
					hasError={this.hasError}
					showAttachment={showAttachment}
					getCustomEmoji={getCustomEmoji}
					navToRoomInfo={navToRoomInfo}
					callJitsi={callJitsi}
					blockAction={blockAction}
					highlighted={highlighted}
					comment={comment}
				/>
			</MessageContext.Provider>
		);
	}
}

export default withTheme(MessageContainer);