/**
 * Extract Facebook Comment data from GraphQL JSON responses.
 */

import type { FbCommentData } from "../../../types.js";
import { dig, asStr, asNum, asObj, edgeNodes, epochToIso } from "./helpers.js";

export function extractComments(
  body: Record<string, unknown>,
): FbCommentData[] {
  try {
    // Multiple known paths for comments
    const commentNodes =
      findCommentNodes(body, "data", "node", "feedback", "display_comments") ??
      findCommentNodes(body, "data", "feedback", "display_comments") ??
      findCommentNodes(body, "data", "node", "comment_rendering_instance_for_feed_location", "comments") ??
      findCommentNodes(body, "node", "feedback", "display_comments");

    if (!commentNodes || commentNodes.length === 0) {
      return [];
    }

    const result: FbCommentData[] = [];
    for (const node of commentNodes) {
      const comment = nodeToComment(node);
      if (comment) result.push(comment);
    }
    return result;
  } catch (err) {
    console.warn("[extractComments] Unexpected error:", (err as Error).message);
    return [];
  }
}

function findCommentNodes(
  body: Record<string, unknown>,
  ...path: string[]
): Array<Record<string, unknown>> | null {
  const connection = dig(body, ...path);
  if (!connection) return null;
  const nodes = edgeNodes<Record<string, unknown>>(connection);
  return nodes.length > 0 ? nodes : null;
}

function nodeToComment(node: Record<string, unknown>): FbCommentData | null {
  const commentId = asStr(node, "id") ?? asStr(node, "comment_id");
  if (!commentId) {
    console.warn("[extractComments] Skipping comment without id");
    return null;
  }

  // Parent post id
  const feedbackNode = dig(node, "feedback") as Record<string, unknown> | undefined;
  const postId =
    asStr(node, "post_id") ??
    asStr(feedbackNode, "id") ??
    asStr(dig(node, "comment_parent", "id") as Record<string, unknown> | undefined) ??
    "";

  // Author
  const author =
    (dig(node, "author") as Record<string, unknown> | undefined) ??
    (dig(node, "body", "delight_ranges", "0", "entity") as Record<string, unknown> | undefined);

  const authorId = asStr(author, "id") ?? "";
  const authorName = asStr(author, "name") ?? "";
  const authorUrl =
    asStr(author, "url") ??
    asStr(author, "uri") ??
    asStr(dig(author, "profile_url") as Record<string, unknown> | undefined);
  const authorPhotoUrl =
    asStr(dig(author, "profile_picture", "uri") as Record<string, unknown> | undefined) ??
    asStr(dig(author, "profile_pic_url") as Record<string, unknown> | undefined);

  // Text
  const text =
    asStr(dig(node, "body", "text") as Record<string, unknown> | undefined) ??
    asStr(dig(node, "message", "text") as Record<string, unknown> | undefined) ??
    asStr(node, "text") ??
    "";

  // Timestamp
  const publishedAt =
    epochToIso(dig(node, "created_time")) ??
    epochToIso(dig(node, "creation_time"));

  // Reactions
  const reactionCount =
    asNum(dig(node, "feedback", "reaction_count", "count") as Record<string, unknown> | undefined) ??
    asNum(dig(node, "feedback", "reactors", "count") as Record<string, unknown> | undefined) ??
    asNum(node, "like_count") ??
    asNum(dig(node, "comment_action_links") as Record<string, unknown> | undefined, "reaction_count");

  // Replies (recursive)
  const replies = extractReplies(node, postId);

  return {
    comment_id: commentId,
    post_id: postId,
    author_id: authorId,
    author_name: authorName,
    author_url: authorUrl,
    author_photo_url: authorPhotoUrl,
    text,
    published_at: publishedAt,
    reaction_count: reactionCount,
    replies: replies.length > 0 ? replies : undefined,
    raw: asObj(node),
  };
}

function extractReplies(
  node: Record<string, unknown>,
  postId: string,
): FbCommentData[] {
  const repliesConnection =
    dig(node, "feedback", "display_comments") ??
    dig(node, "replies");

  if (!repliesConnection) return [];

  const replyNodes = edgeNodes<Record<string, unknown>>(repliesConnection);
  const replies: FbCommentData[] = [];

  for (const rn of replyNodes) {
    // Inject post_id into reply node if missing
    if (!rn.post_id) rn.post_id = postId;
    const reply = nodeToComment(rn);
    if (reply) replies.push(reply);
  }

  return replies;
}
