import { useMemo } from 'react';
import { Comment, type Community } from '@bitsocial/bitsocial-react-hooks';
import { getCommentMediaInfo, getHasThumbnail } from '../lib/utils/media-utils';
import useCommunitiesLoadingStartTimestamps from '../stores/use-communities-loading-start-timestamps-store';
import { useCurrentTime } from './use-current-time';

const MAX_POSTS = 8;
const BOARD_LOADING_TIMEOUT_SECONDS = 30;

// Activity relevance halves every 3 days
const HALF_LIFE_SECONDS = 72 * 3600;

type PopularPostCandidate = {
  boardAddress: string;
  post: Comment;
};

type PopularPostsCacheEntry = {
  randomizedBoardAddresses: string[];
  posts: Comment[];
  revealed: boolean;
};

const popularPostsCacheByInputKey = new Map<string, PopularPostsCacheEntry>();

/**
 * Time-decayed popularity: replyCount divided by age of latest
 * activity so a stale post with many old replies loses to a newer
 * post with a few recent replies.
 */
function popularityScore(post: Comment, nowSeconds: number): number {
  const lastActivity = post.lastReplyTimestamp ?? post.timestamp ?? 0;
  const ageSeconds = Math.max(0, nowSeconds - lastActivity);
  const replies = post.replyCount ?? 0;
  return Math.max(replies, 0.1) / (1 + ageSeconds / HALF_LIFE_SECONDS);
}

function isBoardStillLoading(community: Community | undefined, loadingStartTimestamp: number | undefined, nowSeconds: number): boolean {
  if (community?.updatedAt) {
    return false;
  }

  if (!loadingStartTimestamp) {
    return true;
  }

  return nowSeconds - loadingStartTimestamp < BOARD_LOADING_TIMEOUT_SECONDS;
}

function shuffleBoardAddresses(boardAddresses: string[]): string[] {
  const shuffledBoardAddresses = [...boardAddresses];

  for (let index = shuffledBoardAddresses.length - 1; index > 0; index -= 1) {
    const randomIndex = Math.floor(Math.random() * (index + 1));
    [shuffledBoardAddresses[index], shuffledBoardAddresses[randomIndex]] = [shuffledBoardAddresses[randomIndex], shuffledBoardAddresses[index]];
  }

  return shuffledBoardAddresses;
}

function getPopularPostsInputKey(communityAddresses: string[]): string {
  return [...communityAddresses].sort().join(',');
}

function getPopularPostsCacheEntry(inputKey: string, communityAddresses: string[]): PopularPostsCacheEntry {
  const cachedEntry = popularPostsCacheByInputKey.get(inputKey);
  if (cachedEntry) {
    return cachedEntry;
  }

  const cacheEntry = {
    randomizedBoardAddresses: shuffleBoardAddresses(communityAddresses),
    posts: [],
    revealed: false,
  };
  popularPostsCacheByInputKey.set(inputKey, cacheEntry);
  return cacheEntry;
}

export function getRevealedPopularPosts(communityAddresses: string[]): Comment[] | undefined {
  const cacheEntry = popularPostsCacheByInputKey.get(getPopularPostsInputKey(communityAddresses));
  return cacheEntry?.revealed ? cacheEntry.posts : undefined;
}

export function clearPopularPostsCacheForTest() {
  popularPostsCacheByInputKey.clear();
}

/**
 * Each board contributes at most one time-decayed popular thread, but the
 * board order is shuffled once per page load so repeat navigation keeps the
 * same threads until a real browser refresh creates a fresh module instance.
 *
 * The first revealed set is frozen until the user refreshes or changes
 * the board filter, so threads never disappear during background loads.
 */
const usePopularPosts = (communities: Array<Community | undefined>, communityAddresses: string[]) => {
  const inputKey = getPopularPostsInputKey(communityAddresses);
  const cacheEntry = getPopularPostsCacheEntry(inputKey, communityAddresses);

  const currentTime = useCurrentTime(cacheEntry.revealed ? false : 5);
  const nowSeconds = Math.floor(currentTime);
  const loadingStartTimestamps = useCommunitiesLoadingStartTimestamps(communityAddresses);

  const candidates = useMemo<PopularPostCandidate[]>(() => {
    if (cacheEntry.revealed || cacheEntry.posts.length >= MAX_POSTS) {
      return [];
    }

    try {
      const selectedLinks = new Set<string>();
      const allPosts: PopularPostCandidate[] = [];
      const communitiesByAddress = new Map(communityAddresses.map((boardAddress, index) => [boardAddress, communities[index]]));

      cacheEntry.randomizedBoardAddresses.forEach((boardAddress) => {
        const community = communitiesByAddress.get(boardAddress);
        if (!boardAddress || !community?.posts?.pages?.hot?.comments) {
          return;
        }

        const subPosts: Comment[] = [];
        for (const post of Object.values(community.posts.pages.hot.comments as Record<string, Comment>)) {
          const { deleted, link, linkHeight, linkWidth, locked, pinned, removed, thumbnailUrl } = post;

          try {
            const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
            const hasThumbnail = getHasThumbnail(commentMediaInfo, link);

            if (hasThumbnail && !deleted && !removed && !locked && !pinned) {
              subPosts.push(post);
            }
          } catch {
            // skip posts with malformed media URLs
          }
        }

        subPosts.sort((a, b) => popularityScore(b, nowSeconds) - popularityScore(a, nowSeconds));

        const bestPost = subPosts.find((post) => !selectedLinks.has(post.link));
        if (bestPost) {
          allPosts.push({ boardAddress, post: bestPost });
          selectedLinks.add(bestPost.link);
        }
      });

      return allPosts;
    } catch (err) {
      console.error('Error in usePopularPosts:', err);
      return [];
    }
  }, [nowSeconds, communities, communityAddresses, cacheEntry]);

  const hasPendingBoards = communityAddresses.some((_, index) => isBoardStillLoading(communities[index], loadingStartTimestamps[index], nowSeconds));

  if (!cacheEntry.revealed && (candidates.length >= MAX_POSTS || (!hasPendingBoards && candidates.length > 0))) {
    cacheEntry.posts = candidates.slice(0, MAX_POSTS).map(({ post }) => post);
    cacheEntry.revealed = true;
  }

  const isLoading = !cacheEntry.revealed;

  return { popularPosts: cacheEntry.revealed ? cacheEntry.posts : [], isLoading, error: null as string | null };
};

export default usePopularPosts;
