import 'dayjs/locale/ru';

import { useRef, useState } from 'react';

import dayjs from 'dayjs';
import emojiRegex from 'emoji-regex';
import _ from 'lodash';
import { BsTrash3 } from 'react-icons/bs';
import { FiExternalLink } from 'react-icons/fi';
import {
  IoCheckmarkDoneSharp,
  IoCheckmarkSharp,
} from 'react-icons/io5';
import { LuRotateCw, LuSmilePlus } from 'react-icons/lu';
import { MdAccessTime, MdClose, MdErrorOutline } from 'react-icons/md';

import {
  ExtensionsStore,
  rpc,
} from '../../../api/request';
import {
  FileDocumentFileType,
  ShmChannelMessageType,
  TypeACFHLPSWEnum,
} from '../../../api/types';
import { channelStore, LocalMessage } from '../../../stores/channelStore';
import store from '../../../stores/store';
import { authorColor } from '../../../utils/authorColor';
import { emojiFromId } from '../../../utils/avatat';
import { getBubbleRadiusClasses } from '../../../presentation/bubbleRadius';
import { usePresentation } from '../../../presentation/PresentationContext';
import Avatar from '../../../ui/Avatar';
import Modal from '../../../ui/Modal';
import AudioPlayer from './AudioPlayer';
import FileTypeIcon from './FileTypeIcon';
import MessageReplayItem from './MessageReplayItem';

dayjs.locale('ru');

export const reactionsTypes: {
  name: string;
  label: string;
  value: TypeACFHLPSWEnum;
}[] = [
  { name: '❤️', label: 'Сердце', value: 'heart' },
  { name: '👍', label: 'Лайк', value: 'like' },
  { name: '😂', label: 'Смех', value: 'laugh' },
  { name: '😮', label: 'Удивление', value: 'wow' },
  { name: '😢', label: 'Грусть', value: 'sad' },
  { name: '😡', label: 'Злость', value: 'angry' },
  { name: '👏', label: 'Аплодисменты', value: 'clap' },
  { name: '🎉', label: 'Праздник', value: 'party' },
  { name: '💩', label: 'Стыд', value: 'poop' },
  { name: '🔥', label: 'Огонь', value: 'fire' },
];

const findNameByReaction = (reaction: TypeACFHLPSWEnum) => {
  return reactionsTypes.find((r) => r.value === reaction)?.name || '';
};

export function isValidEmojiString(input?: string): boolean {
  if (!input) return false;
  const regex = emojiRegex();
  const matches = input.match(regex);
  if (!matches) return false;
  const isOnlyEmoji = matches.join('') === input;
  const isValidLength = matches.length <= 8;
  return isOnlyEmoji && isValidLength;
}

type MessageItemProps = {
  onReplayClick: (msg: ShmChannelMessageType) => void;
  msg: LocalMessage;
  onChange: () => void;
  onOpenMenu: (anchor: HTMLElement) => void;
  onReactionClick: (msg: ShmChannelMessageType) => void;
  groupedTop?: boolean;
  groupedBottom?: boolean;
  showAuthorName?: boolean;
};

const SWIPE_ACTIVATE_PX = 60;
const SWIPE_MAX_TRACK_PX = 80;

const isImageAttachment = (doc: FileDocumentFileType) => {
  const prefix = doc.type?.split('/')[0];
  return prefix === 'image' && !!doc.url;
};

const MessageItem = ({
  onReplayClick,
  onReactionClick,
  msg,
  onOpenMenu,
  onChange,
  groupedTop,
  groupedBottom,
  showAuthorName,
}: MessageItemProps) => {
  const {
    id: presentationId,
    components: { BubbleTail },
  } = usePresentation();

  const [imagePreview, setImagePreview] = useState<{
    url: string;
    name?: string;
  } | null>(null);
  const bubbleRef = useRef<HTMLDivElement | null>(null);
  const longPressTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const touchStartRef = useRef<{ x: number; y: number } | null>(null);
  const swipeActiveRef = useRef(false);

  const sendReaction = (reaction: TypeACFHLPSWEnum) => {
    rpc
      .channelReaction({
        message: msg._id,
        channel: msg.channel,
        reaction,
      })
      .then(() => onChange());
  };

  const reply = ExtensionsStore.shmChannelMessagesByID(msg.reply);
  const isDeleted = !!msg.deleted;
  const isSystem = msg.type == 'system';
  const onlyEmoji = !isDeleted && isValidEmojiString(msg.text);
  const isOut = msg.owner == store.userID;
  const isPending = !!msg.__clientId;
  const isFailed = msg.__status === 'failed';
  const showTail = !groupedBottom && !onlyEmoji;
  const showAvatar = !isOut && !groupedBottom;

  if (isSystem) {
    if (presentationId === 'whatsapp') {
      return (
        <div className="flex justify-center my-2">
          <div
            className="max-w-[90%] text-center px-2.5 py-1 rounded-lg text-[12.5px] font-medium shadow-sm tg-fade-in
              bg-white/95 text-[var(--tg-text-secondary)] border border-[var(--tg-divider)]
              dark:bg-[#2a3942] dark:text-[var(--tg-text-secondary)] dark:border-[var(--tg-divider)]"
          >
            {msg.text}
          </div>
        </div>
      );
    }
    if (presentationId === 'instagram') {
      return (
        <div className="flex justify-center my-3 px-4">
          <div
            className="max-w-[min(92%,420px)] text-center px-4 py-1.5 rounded-full text-[11px] font-semibold uppercase tracking-wider tg-fade-in
              bg-[var(--tg-bg-secondary)] text-[var(--tg-text-secondary)] border border-[var(--tg-divider)]
              dark:bg-[#1a1a1a] dark:border-[#333] dark:text-[var(--tg-text-meta)]"
          >
            {msg.text}
          </div>
        </div>
      );
    }
    return (
      <div className="flex justify-center my-3">
        <div className="px-3 py-1 rounded-full bg-black/40 text-white text-[12px] tg-fade-in">
          {msg.text}
        </div>
      </div>
    );
  }

  const radius = getBubbleRadiusClasses(presentationId, {
    isOut,
    groupedTop: !!groupedTop,
    groupedBottom: !!groupedBottom,
    showTail,
  });

  const bubbleColor = isOut
    ? 'bg-[var(--tg-bubble-out)] text-[var(--tg-bubble-out-text)]'
    : 'bg-[var(--tg-bubble-in)] text-[var(--tg-bubble-in-text)]';

  const igBubbleChrome =
    presentationId === 'instagram' && !isDeleted && !onlyEmoji;

  const bubbleFillClass = (() => {
    if (igBubbleChrome) {
      if (isOut) return 'ig-out-bubble text-[var(--tg-bubble-out-text)]';
      return 'ig-in-bubble text-[var(--tg-bubble-in-text)]';
    }
    return bubbleColor;
  })();

  const igBubblePad = igBubbleChrome ? 'px-3 pt-1.5 pb-1.5' : 'px-2.5 pt-1 pb-1';
  const bubbleDropShadow =
    onlyEmoji || presentationId === 'instagram'
      ? ''
      : 'shadow-[var(--tg-bubble-shadow)]';

  const groupedReactions = msg.reactions
    ? _.groupBy(msg.reactions, (r) => r.type)
    : {};

  const myReaction = msg.reactions?.find((r) => r.user == store.userID)?.type;

  const hasText = !!msg.text;
  const hasVoice = !!msg.voice?.url;
  const hasAttachments = !!msg.attachments?.length;
  const hasMedia = hasVoice || hasAttachments;
  const hasReactions = !isDeleted && !!Object.keys(groupedReactions).length;

  // Status icon (sending/sent/read/failed). Только для исходящих.
  let statusIcon: React.ReactNode = null;
  if (isOut && !isDeleted) {
    if (isFailed) {
      statusIcon = <MdErrorOutline size={14} className="text-[var(--tg-danger)]" />;
    } else if (isPending) {
      statusIcon = <MdAccessTime size={13} />;
    } else if (msg.viewed) {
      statusIcon = <IoCheckmarkDoneSharp size={14} />;
    } else {
      statusIcon = <IoCheckmarkSharp size={14} />;
    }
  }

  // Reserved width for inline meta block (HH:MM + checks).
  // Failed status делает блок чуть шире из-за иконки-восклицания.
  const metaWidth = isOut ? (isFailed ? 64 : 56) : 36;

  const metaBlock = !onlyEmoji && (
    <span
      className={`inline-flex items-center gap-0.5 leading-none select-none ${
        presentationId === 'instagram'
          ? 'text-[10px] font-semibold tracking-wide'
          : 'text-[11px]'
      } ${
        isOut
          ? 'text-[var(--tg-text-meta-out)]'
          : 'text-[var(--tg-text-meta)]'
      } opacity-90`}
    >
      <span>{msg.created_at && dayjs.unix(msg.created_at).format('HH:mm')}</span>
      {statusIcon}
    </span>
  );

  // Имя автора (только для входящих в групповом канале) — над bubble.
  const authorName = (() => {
    if (!showAuthorName || isOut) return null;
    if (groupedTop) return null;
    const ownerId = msg.owner ?? '';
    const color = authorColor(ownerId);
    // Имени собеседника у нас нет в API, поэтому стабильный псевдоним по id.
    // (Если в проекте есть Members с именами — сюда подставится readable name.)
    const display = emojiFromId(ownerId) + ' Собеседник';
    return (
      <div
        className="text-[13px] font-semibold leading-4 mb-0.5 truncate"
        style={{ color }}
      >
        {display}
      </div>
    );
  })();

  // ===== Touch interactions: long-press (меню) + swipe-to-reply ============
  const cancelLongPress = () => {
    if (longPressTimerRef.current) {
      clearTimeout(longPressTimerRef.current);
      longPressTimerRef.current = null;
    }
  };

  const resetSwipeTransform = () => {
    if (bubbleRef.current) {
      bubbleRef.current.style.transition = 'transform 0.18s ease';
      bubbleRef.current.style.transform = '';
      setTimeout(() => {
        if (bubbleRef.current) bubbleRef.current.style.transition = '';
      }, 200);
    }
  };

  const onTouchStart = (e: React.TouchEvent) => {
    if (isDeleted || isFailed) return;
    const t = e.touches[0];
    touchStartRef.current = { x: t.clientX, y: t.clientY };
    swipeActiveRef.current = false;
    cancelLongPress();
    longPressTimerRef.current = setTimeout(() => {
      longPressTimerRef.current = null;
      if (!swipeActiveRef.current && bubbleRef.current) {
        // Лёгкая «отдача» + меню
        if (typeof navigator !== 'undefined' && 'vibrate' in navigator) {
          try { navigator.vibrate?.(10); } catch {}
        }
        onOpenMenu(bubbleRef.current);
      }
    }, 380);
  };

  const onTouchMove = (e: React.TouchEvent) => {
    if (!touchStartRef.current || !bubbleRef.current) return;
    const t = e.touches[0];
    const dx = t.clientX - touchStartRef.current.x;
    const dy = t.clientY - touchStartRef.current.y;

    if (Math.abs(dy) > 12 && !swipeActiveRef.current) {
      cancelLongPress();
      return;
    }

    // Свайп влево для исходящих, вправо для входящих
    const direction = isOut ? -1 : 1;
    const projected = dx * direction;
    if (projected > 6) {
      swipeActiveRef.current = true;
      cancelLongPress();
      const tracked = Math.min(projected, SWIPE_MAX_TRACK_PX);
      bubbleRef.current.style.transform = `translateX(${tracked * direction}px)`;
    } else if (swipeActiveRef.current) {
      bubbleRef.current.style.transform = '';
    }
  };

  const onTouchEnd = () => {
    cancelLongPress();
    if (swipeActiveRef.current && bubbleRef.current) {
      const m = bubbleRef.current.style.transform.match(/-?\d+/);
      const dx = m ? Math.abs(parseInt(m[0], 10)) : 0;
      if (dx >= SWIPE_ACTIVATE_PX) {
        channelStore.setReplyMsg(msg);
      }
    }
    resetSwipeTransform();
    swipeActiveRef.current = false;
    touchStartRef.current = null;
  };

  const onTouchCancel = () => {
    cancelLongPress();
    resetSwipeTransform();
    swipeActiveRef.current = false;
    touchStartRef.current = null;
  };

  const onContextMenu = (e: React.MouseEvent) => {
    if (isDeleted) return;
    e.preventDefault();
    if (bubbleRef.current) onOpenMenu(bubbleRef.current);
  };

  return (
    <>
    <div
      className={`flex items-end gap-1.5 ${isOut ? 'justify-end' : 'justify-start'} ${
        presentationId === 'whatsapp'
          ? groupedBottom
            ? 'mb-0.5'
            : 'mb-1.5'
          : presentationId === 'instagram'
            ? groupedBottom
              ? 'mb-1'
              : 'mb-3'
            : groupedBottom
              ? 'mb-px'
              : 'mb-2'
      } tg-fade-in`}
    >
      {/* Avatar slot — only for incoming, only on last in group */}
      {!isOut && (
        <div className="w-9 shrink-0 self-end">
          {showAvatar && <Avatar id={msg.owner ?? ''} size={36} />}
        </div>
      )}

      <div
        id={`msg-${msg._id}`}
        ref={bubbleRef}
        onContextMenu={onContextMenu}
        onTouchStart={onTouchStart}
        onTouchMove={onTouchMove}
        onTouchEnd={onTouchEnd}
        onTouchCancel={onTouchCancel}
        className={`
          group relative max-w-[78%] sm:max-w-[60%] md:max-w-[520px]
          ${onlyEmoji ? 'tg-only-emoji bg-transparent p-0' : `${igBubblePad} ${bubbleFillClass} ${radius}`}
          ${bubbleDropShadow}
          ${isPending ? 'opacity-80' : ''}
        `}
      >
        {/* Author name */}
        {authorName}

        {/* Bubble tail SVG */}
        {showTail && <BubbleTail isOut={isOut} />}

        {/* Reply */}
        {reply && (
          <MessageReplayItem
            msg={reply}
            onClick={() => reply && onReplayClick(reply)}
          />
        )}

        {/* Deleted */}
        {isDeleted ? (
          <div className="flex items-center gap-1.5 italic opacity-70 text-[14px] py-1 pr-[60px] relative">
            <BsTrash3 className="w-3.5 h-3.5" />
            Сообщение удалено
            <span className="absolute bottom-1 right-2 text-[11px] not-italic opacity-80">
              {msg.created_at && dayjs.unix(msg.created_at).format('HH:mm')}
            </span>
          </div>
        ) : (
          <>
            {/* Voice */}
            {hasVoice && (
              <div className="py-1 min-w-[220px]">
                <AudioPlayer
                  barColor={
                    isOut
                      ? 'color-mix(in srgb, var(--tg-accent) 70%, white)'
                      : 'var(--tg-accent)'
                  }
                  src={msg.voice!.url!}
                  cacheKey={msg._id}
                />
              </div>
            )}

            {/* Attachments */}
            {msg.attachments?.map((attachment, i) => {
              const isImg = isImageAttachment(attachment);
              return (
                <div key={i} className="py-1.5">
                  {isImg ? (
                    <>
                      <button
                        type="button"
                        title="Открыть"
                        className={`block w-full p-0 m-0 border-0 bg-transparent cursor-pointer text-left rounded-xl overflow-hidden active:opacity-90 ${
                          presentationId === 'instagram'
                            ? 'ring-1 ring-black/[0.05] dark:ring-white/10'
                            : 'ring-1 ring-black/10 dark:ring-white/15'
                        }`}
                        onClick={(e) => {
                          e.stopPropagation();
                          if (attachment.url) {
                            setImagePreview({
                              url: attachment.url,
                              name: attachment.name,
                            });
                          }
                        }}
                      >
                        <img
                          src={attachment.url}
                          alt={attachment.name || ''}
                          className="max-h-64 w-full object-cover bg-black/5 dark:bg-white/5"
                          loading="lazy"
                          draggable={false}
                        />
                      </button>
                      <div className="flex items-center gap-2 pt-1.5">
                        <div className="flex-1 min-w-0 font-medium text-[14px] truncate">
                          {attachment.name}
                        </div>
                        <button
                          type="button"
                          onClick={(e) => {
                            e.stopPropagation();
                            window.open(attachment.url, '_blank');
                          }}
                          className="p-1 opacity-70 hover:opacity-100 shrink-0"
                          title="Открыть в новой вкладке"
                        >
                          <FiExternalLink size={16} />
                        </button>
                      </div>
                    </>
                  ) : (
                    <div className="flex items-center gap-2 cursor-pointer active:opacity-70">
                      <FileTypeIcon doc={attachment} size={36} />
                      <div className="flex-1 min-w-0">
                        <div className="font-medium text-[14px] truncate">
                          {attachment.name}
                        </div>
                      </div>
                      <button
                        type="button"
                        onClick={(e) => {
                          e.stopPropagation();
                          window.open(attachment.url, '_blank');
                        }}
                        className="p-1 opacity-70 hover:opacity-100"
                      >
                        <FiExternalLink size={16} />
                      </button>
                    </div>
                  )}
                </div>
              );
            })}

            {/* Text with inline meta-spacer (Telegram trick) */}
            {hasText && (
              <div
                className={`relative whitespace-pre-wrap break-words leading-[1.35] ${
                  presentationId === 'instagram' && !onlyEmoji
                    ? 'text-[14px] leading-[1.45]'
                    : 'text-[15px]'
                }`}
                style={onlyEmoji ? { fontSize: 48, lineHeight: 1.1 } : undefined}
              >
                {msg.text}
                {!onlyEmoji && (
                  <>
                    {' '}
                    <span
                      aria-hidden
                      className="inline-block align-bottom select-none"
                      style={{ width: metaWidth, height: '1em' }}
                    >
                      &#8203;
                    </span>
                  </>
                )}
              </div>
            )}

            {/* If no text but has media — pad bottom so abs meta has room */}
            {!hasText && hasMedia && <div style={{ height: 14 }} />}
          </>
        )}

        {/* Reactions */}
        {hasReactions && (
          <div className="flex flex-wrap gap-1 mt-1 mb-0.5 pr-[60px]">
            {Object.entries(groupedReactions).map(([type, list]) => {
              const isMine = myReaction === type;
              const reactionClass =
                presentationId === 'instagram'
                  ? `inline-flex items-center gap-1 h-6 px-2 rounded-full text-[12px] font-semibold tg-press border
                    ${
                      isMine
                        ? isOut
                          ? 'border-white/35 bg-white/15 text-white backdrop-blur-[2px]'
                          : 'border-[var(--tg-accent)]/35 bg-[var(--tg-accent-soft)] text-[var(--tg-accent)]'
                        : isOut
                          ? 'border-white/20 bg-black/12 text-current dark:bg-white/12'
                          : 'border-[var(--tg-divider)] bg-[var(--tg-bg)]/90 text-[var(--tg-text-primary)] shadow-[0_1px_2px_rgba(0,0,0,0.04)] dark:bg-[var(--tg-bg-secondary)]/90'
                    }`
                  : `inline-flex items-center gap-1 h-6 px-1.5 rounded-full
                    text-[12px] font-medium tg-press
                    ${
                      isMine
                        ? isOut
                          ? 'bg-white/25 ring-1 ring-white/60 text-current'
                          : 'bg-[var(--tg-accent)] text-white'
                        : isOut
                          ? 'bg-black/10 dark:bg-white/15 text-current'
                          : 'bg-[var(--tg-accent-soft)] text-[var(--tg-accent)]'
                    }`;
              return (
                <button
                  key={type}
                  type="button"
                  onClick={(e) => {
                    e.stopPropagation();
                    sendReaction(type as TypeACFHLPSWEnum);
                  }}
                  className={reactionClass}
                >
                  <span>{findNameByReaction(type as TypeACFHLPSWEnum)}</span>
                  <span>{list.length}</span>
                </button>
              );
            })}
          </div>
        )}

        {/* Meta — absolute at bottom-right of bubble (covers reserved spacer) */}
        {!onlyEmoji && !isDeleted && (
          <div className="absolute bottom-[5px] right-2 pointer-events-none">
            {metaBlock}
          </div>
        )}

        {/* Meta for emoji-only — inline below */}
        {onlyEmoji && (
          <div className="flex justify-end mt-1 pr-1">{metaBlock}</div>
        )}

        {/* Retry button for failed outgoing */}
        {isFailed && msg.__clientId && (
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation();
              channelStore.retryPending(msg.__clientId!);
            }}
            className={`
              absolute -left-9 bottom-0 w-7 h-7 inline-flex items-center justify-center
              rounded-full text-[var(--tg-danger)]
              ${
                presentationId === 'instagram'
                  ? 'ig-float-action'
                  : 'bg-[var(--tg-bg)] shadow border border-[var(--tg-divider)]'
              }
            `}
            title="Повторить отправку"
          >
            <LuRotateCw className="w-4 h-4" />
          </button>
        )}

        {/* Add reaction (desktop hover only) — теперь для обоих направлений */}
        {!isDeleted && !isPending && (
          <button
            type="button"
            id={`msg-add-reactions-${msg._id}`}
            onClick={(e) => {
              e.stopPropagation();
              onReactionClick(msg);
            }}
            className={`
              hidden md:inline-flex absolute bottom-0
              ${isOut ? '-left-9' : '-right-9'}
              w-7 h-7 items-center justify-center
              rounded-full
              ${
                presentationId === 'instagram'
                  ? 'ig-float-action'
                  : 'bg-[var(--tg-bg)] shadow border border-[var(--tg-divider)]'
              }
              opacity-0 group-hover:opacity-100
              transition-opacity
            `}
          >
            <LuSmilePlus className="w-4 h-4 text-[var(--tg-text-secondary)]" />
          </button>
        )}
      </div>
    </div>

    <Modal
      opened={!!imagePreview}
      onClose={() => setImagePreview(null)}
      className="!w-fit !max-w-[min(96vw,1200px)] !bg-transparent !shadow-none overflow-visible p-1"
    >
      {imagePreview && (
        <div className="relative flex max-w-[min(96vw,1200px)] flex-col items-center">
          <button
            type="button"
            onClick={() => setImagePreview(null)}
            className="absolute -right-1 -top-1 z-10 flex h-9 w-9 items-center justify-center rounded-full
              bg-black/55 text-white shadow-md hover:bg-black/70 active:opacity-90"
            aria-label="Закрыть"
          >
            <MdClose size={22} />
          </button>
          <img
            src={imagePreview.url}
            alt={imagePreview.name || ''}
            className="max-h-[85vh] max-w-[min(92vw,1200px)] w-auto object-contain rounded-lg"
            draggable={false}
          />
        </div>
      )}
    </Modal>
    </>
  );
};

export default MessageItem;
