// @refresh reset
import { useCallback, useEffect, useMemo, useRef, useState, type MouseEvent, type ReactNode, type RefObject } from 'react';
import { useAtom, useAtomValue } from 'jotai';
import { Box, Scroll, Spinner, Text, config } from 'folds';
import { RelationType, Room } from 'matrix-js-sdk';
import { ReactEditor } from 'slate-react';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { usePowerLevels } from '../../hooks/usePowerLevels';
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
import { useRoomCreators } from '../../hooks/useRoomCreators';
import { useImagePackRooms } from '../../hooks/useImagePackRooms';
import { roomToParentsAtom } from '../../state/room/roomToParents';
import { roomIdToReplyDraftAtomFamily } from '../../state/room/roomInputDrafts';
import { canEditEvent, getEditedEvent } from '../../utils/room';
import { MessageEditor } from '../room/message/MessageEditor';
import { Reactions } from '../room/message';
import { formatForumTimeAgo } from './forumTime';
import { loadPostThread, loadPostThreadLocal } from './forumFeed';
import { countAllReplies } from './forumTopicHelpers';
import type { ForumPost } from './types';
import { ForumAuthorIdentity } from './ForumAuthorIdentity';
import { ForumEventContent } from './ForumEventContent';
import { ForumMessageContextMenu } from './ForumMessageContextMenu';
import { ForumMessageActions } from './ForumMessageActions';
import { ForumReplyComposer } from './ForumReplyComposer';
import { useForumMessageReactions } from './useForumMessageReactions';
import { useForumRoomLiveUpdates } from './useForumRoomLiveUpdates';
import { resetEditor, resetEditorHistory, useEditor } from '../../components/editor';
import { mobileOrTablet } from '../../utils/user-agent';
import * as theme from './forumTheme.css';

function forumBranchKey(eventId: string): string {
  return `branch:${eventId}`;
}

function forumMessageSurfaceClass(
  baseClass: string,
  eventId: string,
  replyTargetEventId?: string
): string {
  if (replyTargetEventId && replyTargetEventId === eventId) {
    return `${baseClass} ${theme.ForumThreadReplyTarget}`;
  }
  return baseClass;
}

type ForumThreadBranchProps = {
  className: string;
  railClassName: string;
  collapsed: boolean;
  hiddenCount: number;
  onToggle: () => void;
  children: ReactNode;
};

function morePostsLabel(count: number): string {
  return `${count} more ${count === 1 ? 'post' : 'posts'}`;
}

function ForumThreadBranch({
  className,
  railClassName,
  collapsed,
  hiddenCount,
  onToggle,
  children,
}: ForumThreadBranchProps) {
  const isRootBranch = className === theme.ForumThread;

  const handleToggle = (event: MouseEvent) => {
    event.stopPropagation();
    onToggle();
  };

  if (collapsed && hiddenCount > 0) {
    return (
      <button
        type="button"
        className={
          isRootBranch ? theme.ForumThreadCollapsedRowRoot : theme.ForumThreadCollapsedRowNested
        }
        aria-label={`Show ${morePostsLabel(hiddenCount)}`}
        title={`Show ${morePostsLabel(hiddenCount)}`}
        onClick={handleToggle}
      >
        <span
          className={
            isRootBranch ? theme.ForumThreadCollapsedRailRoot : theme.ForumThreadCollapsedRailNested
          }
          aria-hidden
        />
        <span className={theme.ForumThreadCollapsedMiniPost}>
          <span className={theme.ForumThreadCollapsedMoreLabel}>{morePostsLabel(hiddenCount)}</span>
        </span>
      </button>
    );
  }

  return (
    <div className={`${className} ${theme.ForumThreadCollapsible}`}>
      <button
        type="button"
        className={railClassName}
        aria-label="Collapse replies"
        title="Collapse replies"
        onClick={handleToggle}
      />
      {children}
    </div>
  );
}

type ForumThreadMessageProps = {
  post: ForumPost;
  room: Room;
  roomId: string;
  threadRootId: string;
  fileDropContainerRef: RefObject<HTMLElement>;
  editEventId?: string;
  onEditEventId: (eventId?: string) => void;
  onComposerSent: () => void;
  onDeleted: () => void;
  onReplyTo: (eventId: string) => void;
  replyTargetEventId?: string;
  collapsedBranches: Set<string>;
  onToggleBranch: (branchKey: string) => void;
};

function ForumThreadMessage({
  post,
  room,
  roomId,
  threadRootId,
  fileDropContainerRef,
  editEventId,
  onEditEventId,
  onComposerSent,
  onDeleted,
  onReplyTo,
  replyTargetEventId,
  collapsedBranches,
  onToggleBranch,
}: ForumThreadMessageProps) {
  const mx = useMatrixClient();
  const mEvent = room.findEventById(post.eventId);
  const isEditing = editEventId === post.eventId;
  const branchKey = forumBranchKey(post.eventId);
  const replyCount = countAllReplies(post.replies);
  const repliesCollapsed = collapsedBranches.has(branchKey);
  const powerLevels = usePowerLevels(room);
  const creators = useRoomCreators(room);
  const permissions = useRoomPermissions(creators, powerLevels);
  const roomToParents = useAtomValue(roomToParentsAtom);
  const imagePackRooms = useImagePackRooms(roomId, roomToParents);

  const canEdit = mEvent ? canEditEvent(mx, mEvent) : false;
  const canDelete = mEvent
    ? permissions.action('redact', mx.getSafeUserId()) || mEvent.getSender() === mx.getUserId()
    : false;
  const { canSendReaction, reactionRelations, handleReactionToggle } = useForumMessageReactions(
    room,
    mEvent
  );

  const handleDelete = useCallback(() => {
    if (!mEvent) return;
    const eventId = mEvent.getId();
    if (!eventId) return;
    mx.redactEvent(roomId, eventId).then(onDeleted);
  }, [mEvent, mx, onDeleted, roomId]);

  const handleEditClose = useCallback(() => {
    onEditEventId(undefined);
    onComposerSent();
  }, [onComposerSent, onEditEventId]);

  return (
    <article className={theme.ForumThreadReply} data-event-id={post.eventId}>
      <ForumMessageContextMenu
        room={room}
        mEvent={mEvent}
        imagePackRooms={imagePackRooms}
        disabled={isEditing}
        canEdit={canEdit}
        canDelete={canDelete}
        onReply={() => onReplyTo(post.eventId)}
        onEdit={() => onEditEventId(post.eventId)}
        onDeleted={onDeleted}
        className={forumMessageSurfaceClass(theme.ForumThreadReplyInner, post.eventId, replyTargetEventId)}
      >
        <header className={theme.ForumPostHeader}>
          <div className={theme.ForumPostHeaderMain}>
            <ForumAuthorIdentity room={room} sender={post.sender} displayName={post.senderDisplayName} />
            <time className={theme.ForumPostTime} dateTime={new Date(post.timestamp).toISOString()}>
              {formatForumTimeAgo(post.timestamp)}
            </time>
          </div>
        </header>
        {post.replyToDeleted && (
          <p className={theme.ForumReplyToDeleted} role="note">
            Replying to a deleted message
          </p>
        )}
        {isEditing && mEvent ? (
          <MessageEditor
            room={room}
            roomId={roomId}
            mEvent={mEvent}
            imagePackRooms={imagePackRooms}
            onCancel={handleEditClose}
          />
        ) : (
          <>
            <ForumEventContent
              room={room}
              eventId={post.eventId}
              body={post.body}
              bodyHtml={post.bodyHtml}
              senderDisplayName={post.senderDisplayName}
            />
            {reactionRelations && (
              <Reactions
                style={{ marginTop: config.space.S200 }}
                room={room}
                relations={reactionRelations}
                mEventId={post.eventId}
                canSendReaction={canSendReaction}
                onReactionToggle={handleReactionToggle}
              />
            )}
          </>
        )}
        {!isEditing && (
          <div className={theme.ForumMessageOptionsBase}>
            <ForumMessageActions
              imagePackRooms={imagePackRooms}
              eventId={post.eventId}
              body={post.body}
              canSendReaction={canSendReaction}
              onReactionToggle={handleReactionToggle}
              onReply={() => onReplyTo(post.eventId)}
              replyActive={replyTargetEventId === post.eventId}
              onEdit={() => onEditEventId(post.eventId)}
              canEdit={canEdit}
              onDelete={handleDelete}
              canDelete={canDelete}
            />
          </div>
        )}
      </ForumMessageContextMenu>
      {post.replies.length > 0 && (
        <ForumThreadBranch
          className={theme.ForumThreadChildren}
          railClassName={theme.ForumThreadRailHitNested}
          collapsed={repliesCollapsed}
          hiddenCount={replyCount}
          onToggle={() => onToggleBranch(branchKey)}
        >
          {!repliesCollapsed &&
            post.replies.map((child) => (
              <ForumThreadMessage
                key={child.eventId}
                post={child}
                room={room}
                roomId={roomId}
                threadRootId={threadRootId}
                fileDropContainerRef={fileDropContainerRef}
                editEventId={editEventId}
                onEditEventId={onEditEventId}
                onComposerSent={onComposerSent}
                onDeleted={onDeleted}
                onReplyTo={onReplyTo}
                replyTargetEventId={replyTargetEventId}
                collapsedBranches={collapsedBranches}
                onToggleBranch={onToggleBranch}
              />
            ))}
        </ForumThreadBranch>
      )}
    </article>
  );
}

type ForumThreadDetailProps = {
  roomId: string;
  rootEventId: string;
  titleHint?: string;
  initialPost?: ForumPost | null;
  fileDropContainerRef?: RefObject<HTMLElement>;
  onReplySent?: (reply: ForumPost) => void;
};

type ForumThreadDetailBodyProps = ForumThreadDetailProps & {
  post: ForumPost;
  room: Room;
  onPostUpdate: (post: ForumPost | null) => void;
};

function ForumThreadDetailBody({
  roomId,
  rootEventId,
  titleHint,
  post,
  room,
  fileDropContainerRef,
  onReplySent,
  onPostUpdate,
}: ForumThreadDetailBodyProps) {
  const mx = useMatrixClient();
  const editor = useEditor();
  const [editEventId, setEditEventId] = useState<string | undefined>();
  const [replyToEventId, setReplyToEventId] = useState(rootEventId);
  const [replyDraft, setReplyDraft] = useAtom(roomIdToReplyDraftAtomFamily(roomId));
  const timelineSet = useMemo(() => room.getUnfilteredTimelineSet(), [room]);

  useEffect(
    () => () => {
      resetEditor(editor);
      resetEditorHistory(editor);
    },
    [editor]
  );

  useEffect(() => {
    setReplyToEventId(rootEventId);
    setReplyDraft(undefined);
  }, [rootEventId, setReplyDraft]);

  useEffect(() => {
    if (!replyDraft) {
      setReplyToEventId(rootEventId);
    }
  }, [replyDraft, rootEventId]);
  const [contentRevision, setContentRevision] = useState(0);
  const [collapsedBranches, setCollapsedBranches] = useState<Set<string>>(() => new Set());
  const localDropRef = useRef<HTMLDivElement>(null);
  const dropRef = fileDropContainerRef ?? localDropRef;
  const powerLevels = usePowerLevels(room);
  const creators = useRoomCreators(room);
  const permissions = useRoomPermissions(creators, powerLevels);
  const roomToParents = useAtomValue(roomToParentsAtom);
  const imagePackRooms = useImagePackRooms(roomId, roomToParents);

  const reloadThread = useCallback(async () => {
    const local = loadPostThreadLocal(mx, roomId, rootEventId);
    if (local) onPostUpdate(local);

    const loaded = await loadPostThread(mx, roomId, rootEventId);
    onPostUpdate(loaded);
    return loaded;
  }, [mx, onPostUpdate, roomId, rootEventId]);

  const handleComposerSent = useCallback(async () => {
    const applyLoaded = (loaded: ForumPost | null) => {
      if (!loaded) return;
      onReplySent?.({
        eventId: loaded.eventId,
        title: loaded.title,
        body: loaded.body,
        sender: loaded.sender,
        timestamp: Date.now(),
        replies: loaded.replies,
      });
    };

    const loaded = await reloadThread();
    applyLoaded(loaded);

    // Local echo can land slightly after send; refresh once more.
    window.setTimeout(() => {
      void reloadThread().then(applyLoaded);
    }, 400);
  }, [onReplySent, reloadThread]);

  const handleDeleted = useCallback(() => {
    void handleComposerSent();
  }, [handleComposerSent]);

  useForumRoomLiveUpdates(
    [room],
    () => {
      setContentRevision((revision) => revision + 1);
      void reloadThread();
    },
    { threadRootId: rootEventId, wait: 150 }
  );

  const rootEvent = room.findEventById(rootEventId);
  const rootEditing = editEventId === rootEventId;
  const canEditRoot = rootEvent ? canEditEvent(mx, rootEvent) : false;
  const canDeleteRoot = rootEvent
    ? permissions.action('redact', mx.getSafeUserId()) || rootEvent.getSender() === mx.getUserId()
    : false;
  const { canSendReaction, reactionRelations, handleReactionToggle } = useForumMessageReactions(
    room,
    rootEvent
  );

  const handleDeleteRoot = useCallback(() => {
    if (!rootEvent) return;
    const eventId = rootEvent.getId();
    if (!eventId) return;
    mx.redactEvent(roomId, eventId).then(() => {
      void handleComposerSent();
    });
  }, [handleComposerSent, mx, rootEvent, roomId]);

  const handleEditRootClose = useCallback(() => {
    setEditEventId(undefined);
    setContentRevision((revision) => revision + 1);
    void handleComposerSent();
  }, [handleComposerSent]);

  const toggleBranch = useCallback((branchKey: string) => {
    setCollapsedBranches((prev) => {
      const next = new Set(prev);
      if (next.has(branchKey)) next.delete(branchKey);
      else next.add(branchKey);
      return next;
    });
  }, []);

  const handleReplyTo = useCallback(
    (eventId: string) => {
      const replyEvt = room.findEventById(eventId);
      if (!replyEvt) return;

      const editedReply = getEditedEvent(eventId, replyEvt, timelineSet);
      const content = editedReply?.getContent()['m.new_content'] ?? replyEvt.getContent();
      const { body, formatted_body: formattedBody } = content;
      const senderId = replyEvt.getSender();

      setReplyToEventId(eventId);
      if (senderId && typeof body === 'string') {
        setReplyDraft({
          userId: senderId,
          eventId,
          body,
          formattedBody,
          relation: { rel_type: RelationType.Thread, event_id: rootEventId },
        });
      }

      window.setTimeout(() => {
        const target = document.querySelector(`[data-event-id="${eventId}"]`);
        target?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
        if (!mobileOrTablet()) ReactEditor.focus(editor);
      }, 0);
    },
    [editor, rootEventId, room, setReplyDraft, timelineSet]
  );

  const rootBranchKey = forumBranchKey(rootEventId);
  const rootReplyCount = countAllReplies(post.replies);
  const rootRepliesCollapsed = collapsedBranches.has(rootBranchKey);

  const replyTargetEventId = replyDraft?.eventId;

  const title = useMemo(() => {
    const local = loadPostThreadLocal(mx, roomId, rootEventId);
    return local?.title || post.title || titleHint || '(untitled post)';
  }, [contentRevision, mx, post.title, roomId, rootEventId, titleHint]);

  return (
    <article ref={localDropRef} className={theme.ForumTopicDetailRoot} data-event-id={post.eventId}>
      <Scroll
        className={theme.ForumTopicDetailBodyScroll}
        variant="Surface"
        direction="Vertical"
        size="300"
        hideTrack
        visibility="Hover"
      >
        <div className={theme.ForumTopicDetailBody}>
      <header className={theme.ForumTopicDetailHead}>
        <div className={theme.ForumTopicDetailHeadMain}>
          <h1 className={theme.ForumDetailTitle}>{title}</h1>
          <div className={theme.ForumTopicDetailAuthor}>
            <ForumAuthorIdentity room={room} sender={post.sender} displayName={post.senderDisplayName} />
            <span className={theme.ForumPostTime}>· {formatForumTimeAgo(post.timestamp)}</span>
          </div>
        </div>
      </header>
      <ForumMessageContextMenu
        room={room}
        mEvent={rootEvent}
        imagePackRooms={imagePackRooms}
        disabled={rootEditing}
        canEdit={canEditRoot}
        canDelete={canDeleteRoot}
        onReply={() => handleReplyTo(post.eventId)}
        onEdit={() => setEditEventId(rootEventId)}
        onDeleted={handleDeleted}
        className={forumMessageSurfaceClass(theme.ForumTopicDetailOp, post.eventId, replyTargetEventId)}
      >
        {rootEditing && rootEvent ? (
          <MessageEditor
            room={room}
            roomId={roomId}
            mEvent={rootEvent}
            imagePackRooms={imagePackRooms}
            onCancel={handleEditRootClose}
          />
        ) : (
          <>
            <ForumEventContent
              room={room}
              eventId={post.eventId}
              body={post.body}
              bodyHtml={post.bodyHtml}
              senderDisplayName={post.senderDisplayName}
            />
            {reactionRelations && (
              <Reactions
                style={{ marginTop: config.space.S200 }}
                room={room}
                relations={reactionRelations}
                mEventId={post.eventId}
                canSendReaction={canSendReaction}
                onReactionToggle={handleReactionToggle}
              />
            )}
          </>
        )}
        {!rootEditing && (
          <div className={theme.ForumMessageOptionsBase}>
            <ForumMessageActions
              imagePackRooms={imagePackRooms}
              eventId={post.eventId}
              body={post.body}
              canSendReaction={canSendReaction}
              onReactionToggle={handleReactionToggle}
              onReply={() => handleReplyTo(post.eventId)}
              replyActive={replyTargetEventId === post.eventId}
              onEdit={() => setEditEventId(rootEventId)}
              canEdit={canEditRoot}
              onDelete={handleDeleteRoot}
              canDelete={canDeleteRoot}
            />
          </div>
        )}
      </ForumMessageContextMenu>
      {post.replies.length > 0 && (
        <ForumThreadBranch
          className={theme.ForumThread}
          railClassName={theme.ForumThreadRailHitRoot}
          collapsed={rootRepliesCollapsed}
          hiddenCount={rootReplyCount}
          onToggle={() => toggleBranch(rootBranchKey)}
        >
          {!rootRepliesCollapsed &&
            post.replies.map((reply) => (
              <ForumThreadMessage
                key={reply.eventId}
                post={reply}
                room={room}
                roomId={roomId}
                threadRootId={rootEventId}
                fileDropContainerRef={dropRef}
                editEventId={editEventId}
                onEditEventId={setEditEventId}
                onComposerSent={() => {
                  void handleComposerSent();
                }}
                onDeleted={handleDeleted}
                onReplyTo={handleReplyTo}
                replyTargetEventId={replyTargetEventId}
                collapsedBranches={collapsedBranches}
                onToggleBranch={toggleBranch}
              />
            ))}
        </ForumThreadBranch>
      )}
        </div>
      </Scroll>
      <div className={theme.ForumThreadFooterComposer}>
        <ForumReplyComposer
          editor={editor}
          room={room}
          roomId={roomId}
          threadRootId={rootEventId}
          replyToEventId={replyToEventId}
          fileDropContainerRef={dropRef}
          onSent={() => {
            void handleComposerSent();
          }}
        />
      </div>
    </article>
  );
}

export function ForumThreadDetail({
  roomId,
  rootEventId,
  titleHint,
  initialPost,
  fileDropContainerRef,
  onReplySent,
}: ForumThreadDetailProps) {
  const mx = useMatrixClient();
  const [post, setPost] = useState<ForumPost | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const room = mx.getRoom(roomId);

  useEffect(() => {
    let cancelled = false;

    if (initialPost?.eventId === rootEventId) {
      setPost(initialPost);
      setError(null);
      setLoading(false);
      return () => {
        cancelled = true;
      };
    }

    setLoading(true);
    setError(null);
    loadPostThread(mx, roomId, rootEventId)
      .then((loaded) => {
        if (cancelled) return;
        setPost(loaded);
        if (!loaded) setError('Post not found.');
      })
      .catch((err) => {
        if (!cancelled) {
          setError(err instanceof Error ? err.message : 'Could not load this thread.');
        }
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [mx, roomId, rootEventId, initialPost]);

  if (loading) {
    return (
      <Box alignItems="Center" gap="200" style={{ padding: '1rem' }}>
        <Spinner size="200" />
        <Text size="T300" priority="300">
          Loading…
        </Text>
      </Box>
    );
  }

  if (error || !post || !room) {
    return (
      <Text as="p" className={theme.ForumListLoading} size="T300" priority="300">
        {error || 'Post not found.'}
      </Text>
    );
  }

  return (
    <ForumThreadDetailBody
      roomId={roomId}
      rootEventId={rootEventId}
      titleHint={titleHint}
      post={post}
      room={room}
      fileDropContainerRef={fileDropContainerRef}
      onReplySent={onReplySent}
      onPostUpdate={setPost}
    />
  );
}
