import { memo, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Comment, Role, useComment, useEditedComment, useSubplebbit } from '@bitsocialnet/bitsocial-react-hooks';
import useSubplebbitsPagesStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/subplebbits-pages';
import { useSubplebbitField } from '../../hooks/use-stable-subplebbit';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { isAllView } from '../../lib/utils/view-utils';
import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
import { useDirectories } from '../../hooks/use-directories';
import { areSameBoardAddress, isDirectoryBoard } from '../../lib/utils/route-utils';
import useIsMobile from '../../hooks/use-is-mobile';
import ErrorDisplay from '../../components/error-display/error-display';
import { PageFooterDesktop, ThreadFooterFirstRow, ThreadFooterStyleRow, ThreadFooterMobile } from '../../components/footer';
import PostDesktop from '../../components/post-desktop';
import PostMobile from '../../components/post-mobile';
import { getRequestedThreadTopCid, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
import styles from './post.module.css';

// useComment may not return cached feed data immediately due to its updatedAt comparison logic.
// This hook falls back to the subplebbit pages store (populated by useFeed) so content
// from the catalog appears instantly instead of going through a loading phase.
const useCommentWithFeedCache = (options: { commentCid: string | undefined }) => {
  const comment = useComment(options);
  const cachedComment = useSubplebbitsPagesStore((state) => state.comments[options?.commentCid || '']);

  return useMemo(() => {
    if (!cachedComment || comment?.timestamp) return comment;
    return { ...cachedComment, state: comment?.state, error: comment?.error, errors: comment?.errors } as Comment;
  }, [comment, cachedComment]);
};

export interface PostProps {
  index?: number;
  isHidden?: boolean;
  hasThumbnail?: boolean;
  post?: any;
  postReplyCount?: number;
  reply?: any;
  roles?: Role[];
  showAllReplies?: boolean;
  showReplies?: boolean;
  targetReplyCid?: string;
  threadNumber?: number;
  isModQueue?: boolean;
  modQueueStatus?: 'approved' | 'rejected' | 'failed' | null;
  modQueueError?: string;
  isPublishing?: boolean;
  onApprove?: () => void;
  onReject?: () => void;
  quotedByMap?: Map<string, Comment[]>;
}

export const Post = memo(
  ({ post, showAllReplies = false, showReplies = true, targetReplyCid, isModQueue, modQueueStatus, modQueueError, isPublishing, onApprove, onReject }: PostProps) => {
    // Only subscribe to roles field to avoid rerenders from updatingState changes
    const roles = useSubplebbitField(post?.subplebbitAddress, (subplebbit) => subplebbit?.roles);
    const isMobile = useIsMobile();

    let comment = post;

    // handle pending mod or author edit
    const { editedComment } = useEditedComment({ comment });
    if (editedComment) {
      comment = editedComment;
    }

    return (
      <div className={styles.thread}>
        <div className={styles.postContainer}>
          {isMobile ? (
            <PostMobile
              post={comment}
              roles={roles}
              showAllReplies={showAllReplies}
              showReplies={showReplies}
              targetReplyCid={targetReplyCid}
              isModQueue={isModQueue}
              modQueueStatus={modQueueStatus}
              modQueueError={modQueueError}
              isPublishing={isPublishing}
              onApprove={onApprove}
              onReject={onReject}
            />
          ) : (
            <PostDesktop
              post={comment}
              roles={roles}
              showAllReplies={showAllReplies}
              showReplies={showReplies}
              targetReplyCid={targetReplyCid}
              isModQueue={isModQueue}
              modQueueStatus={modQueueStatus}
              modQueueError={modQueueError}
              isPublishing={isPublishing}
              onApprove={onApprove}
              onReject={onReject}
            />
          )}
        </div>
      </div>
    );
  },
  (prevProps, nextProps) => {
    const prev = prevProps.post;
    const next = nextProps.post;
    return (
      prev?.cid === next?.cid &&
      prev?.replyCount === next?.replyCount &&
      prev?.updatedAt === next?.updatedAt &&
      prev?.locked === next?.locked &&
      prev?.pinned === next?.pinned &&
      prev?.removed === next?.removed &&
      prev?.deleted === next?.deleted &&
      prev?.commentModeration?.purged === next?.commentModeration?.purged &&
      prevProps.showAllReplies === nextProps.showAllReplies &&
      prevProps.showReplies === nextProps.showReplies &&
      prevProps.targetReplyCid === nextProps.targetReplyCid &&
      prevProps.isModQueue === nextProps.isModQueue &&
      prevProps.modQueueStatus === nextProps.modQueueStatus &&
      prevProps.modQueueError === nextProps.modQueueError &&
      prevProps.isPublishing === nextProps.isPublishing &&
      prevProps.onApprove === nextProps.onApprove &&
      prevProps.onReject === nextProps.onReject
    );
  },
);

const PostPage = () => {
  const { t } = useTranslation();
  const params = useParams();
  const location = useLocation();
  const { commentCid } = params;
  const subplebbitAddress = useResolvedSubplebbitAddress();
  const isInAllView = isAllView(location.pathname);

  const comment = useCommentWithFeedCache({ commentCid });
  const consumedThreadTopScrollRef = useRef<string | null>(null);

  const navigate = useNavigate();
  useEffect(() => {
    if (comment?.subplebbitAddress && subplebbitAddress && !areSameBoardAddress(comment.subplebbitAddress, subplebbitAddress)) {
      navigate('/not-found', { replace: true });
    }
  }, [comment?.subplebbitAddress, subplebbitAddress, navigate]);

  const subplebbit = useSubplebbit({ subplebbitAddress });
  const { error: subplebbitError, shortAddress, title } = subplebbit || {};
  const directories = useDirectories();

  // if the comment is a reply, return the post comment instead, then the reply will be highlighted in the thread
  const postComment = useCommentWithFeedCache({ commentCid: comment?.postCid });
  let post: Comment;
  if (comment.parentCid) {
    post = postComment;
  } else {
    post = comment;
  }
  const requestedThreadTopCid = getRequestedThreadTopCid(location.state);

  const { error } = post || {};

  // These two effects split normal opens from explicit OP-top intents:
  // the first keeps ordinary thread visits on `window.scrollTo(0, 0)`, while the
  // second consumes `requestedThreadTopCid` once per `location.key` via
  // `consumedThreadTopScrollRef` so `scrollThreadContainerToTop(commentCid)` only
  // replays for deliberate OP-link clicks and never for route-driven thread opens.
  useEffect(() => {
    if (!comment?.cid || comment.parentCid) return;
    if (requestedThreadTopCid === comment.cid) return;
    window.scrollTo(0, 0);
  }, [comment?.cid, comment?.parentCid, requestedThreadTopCid]);

  useEffect(() => {
    if (!commentCid || post?.cid !== commentCid) return;
    if (requestedThreadTopCid !== commentCid) return;

    const consumedKey = `${location.key}:${commentCid}`;
    if (consumedThreadTopScrollRef.current === consumedKey) return;

    if (scrollThreadContainerToTop(commentCid)) {
      consumedThreadTopScrollRef.current = consumedKey;
    }
  }, [commentCid, location.key, post?.cid, requestedThreadTopCid]);

  useEffect(() => {
    const boardIdentifier = params.boardIdentifier;
    const isDirectory = boardIdentifier ? isDirectoryBoard(boardIdentifier, directories) : false;

    let boardTitle: string;
    if (isInAllView) {
      boardTitle = t('all');
    } else if (isDirectory) {
      boardTitle = `/${boardIdentifier}/`;
    } else {
      boardTitle = title ? title : shortAddress || subplebbitAddress || '';
    }

    const postTitle = post?.title?.slice(0, 30) || post?.content?.slice(0, 30);
    const postTitlePart = postTitle ? ` - ${postTitle.trim()}...` : '';
    document.title = `${boardTitle}${postTitlePart} - 5chan`;
  }, [title, shortAddress, subplebbitAddress, post?.title, post?.content, isInAllView, t, params.boardIdentifier, directories]);

  const shouldShowCommentError = comment?.error?.message && !comment?.cid;
  const shouldShowPostError = post?.error && post?.replyCount > 0 && post?.replies?.length === 0;
  const shouldShowSubplebbitError = subplebbitError?.message && !post?.cid;

  const targetReplyCid = comment?.parentCid ? comment?.cid : undefined;

  return (
    <div className={styles.content}>
      {shouldShowPostError && (
        <div className={styles.error}>
          <ErrorDisplay error={error} />
        </div>
      )}
      <Post post={post} showAllReplies={true} targetReplyCid={targetReplyCid} />
      {shouldShowSubplebbitError && (
        <div className={styles.error}>
          <ErrorDisplay error={subplebbitError} />
        </div>
      )}
      {shouldShowCommentError && (
        <div className={styles.error}>
          <ErrorDisplay error={comment?.error} />
        </div>
      )}
      {post?.cid && subplebbitAddress ? (
        <>
          <PageFooterDesktop
            firstRow={<ThreadFooterFirstRow postCid={post.cid} threadNumber={post?.number} subplebbitAddress={subplebbitAddress} isThreadClosed={!!post?.locked} />}
            styleRow={<ThreadFooterStyleRow />}
          />
          <ThreadFooterMobile postCid={post.cid} threadNumber={post?.number} subplebbitAddress={subplebbitAddress} isThreadClosed={!!post?.locked} />
        </>
      ) : null}
    </div>
  );
};

export default PostPage;
