import { ThreadsReply } from "../api/haixun";

export type ReplyTreeNode = ThreadsReply & {
  depth: number;
  children: ReplyTreeNode[];
};

export function parentReplyId(reply: ThreadsReply): string {
  const repliedTo = (reply.replied_to_id || "").trim();
  if (repliedTo) return repliedTo;
  return (reply.parent_id || "").trim();
}

export function replyTimeMs(reply: ThreadsReply): number {
  const ms = Date.parse((reply.timestamp || "").trim());
  return Number.isFinite(ms) ? ms : 0;
}

function sortChildrenChronologically(nodes: ReplyTreeNode[]): void {
  nodes.sort((a, b) => replyTimeMs(a) - replyTimeMs(b) || a.id.localeCompare(b.id));
  for (const node of nodes) {
    sortChildrenChronologically(node.children);
  }
}

function sortRootsNewestFirst(roots: ReplyTreeNode[]): void {
  roots.sort((a, b) => replyTimeMs(b) - replyTimeMs(a) || b.id.localeCompare(a.id));
}

export function isOwnReply(reply: ThreadsReply, accountUsername?: string): boolean {
  if (reply.is_reply_owned_by_me) {
    return true;
  }
  const mine = (accountUsername || "").trim().replace(/^@/, "").toLowerCase();
  const author = (reply.username || "").trim().replace(/^@/, "").toLowerCase();
  return Boolean(mine && author && mine === author);
}

export function buildReplyTree(replies: ThreadsReply[], rootMediaId: string): ReplyTreeNode[] {
  const rootId = rootMediaId.trim();
  const nodes = new Map<string, ReplyTreeNode>();
  for (const reply of replies) {
    nodes.set(reply.id, { ...reply, depth: 0, children: [] });
  }

  const roots: ReplyTreeNode[] = [];
  for (const reply of replies) {
    const node = nodes.get(reply.id);
    if (!node) continue;
    const parentId = parentReplyId(reply);
    if (!parentId || parentId === rootId) {
      roots.push(node);
      continue;
    }
    const parent = nodes.get(parentId);
    if (parent) {
      parent.children.push(node);
    } else {
      roots.push(node);
    }
  }

  const setDepth = (items: ReplyTreeNode[], depth: number) => {
    for (const item of items) {
      item.depth = depth;
      setDepth(item.children, depth + 1);
    }
  };
  setDepth(roots, 0);
  sortChildrenChronologically(roots);
  sortRootsNewestFirst(roots);
  return roots;
}

export function ownRepliesForPost(replies: ThreadsReply[], accountUsername?: string): ThreadsReply[] {
  return replies.filter((reply) => isOwnReply(reply, accountUsername));
}

export function hasOwnReplyTo(replies: ThreadsReply[], targetId: string, accountUsername?: string): boolean {
  const target = targetId.trim();
  if (!target) return false;
  return replies.some((reply) => isOwnReply(reply, accountUsername) && parentReplyId(reply) === target);
}

export function flattenReplyTree(nodes: ReplyTreeNode[]): ReplyTreeNode[] {
  const out: ReplyTreeNode[] = [];
  const walk = (items: ReplyTreeNode[]) => {
    for (const item of items) {
      out.push(item);
      walk(item.children);
    }
  };
  walk(nodes);
  return out;
}

export type ReplyTargetKind = "post" | "me" | "user" | "unknown";

export type ReplyTarget = {
  kind: ReplyTargetKind;
  label: string;
  username?: string;
  preview?: string;
};

export function describeReplyTarget(
  reply: ThreadsReply,
  postMediaId: string,
  postText: string,
  replyById: Map<string, ThreadsReply>,
  accountUsername?: string
): ReplyTarget {
  const parentId = parentReplyId(reply);
  const rootId = postMediaId.trim();
  if (!parentId || parentId === rootId) {
    return { kind: "post", label: "留言在你的貼文", preview: postText };
  }
  const parent = replyById.get(parentId);
  if (!parent) {
    return { kind: "unknown", label: "回覆上一則（未載入）" };
  }
  if (isOwnReply(parent, accountUsername)) {
    return { kind: "me", label: "在問你", username: accountUsername, preview: parent.text };
  }
  const parentUser = (parent.username || "").trim();
  return {
    kind: "user",
    label: parentUser ? `回覆 @${parentUser}` : "回覆上一則留言",
    username: parent.username,
    preview: parent.text
  };
}

export type ReplyLane = "mine" | "visitor" | "asking_me";

export function replyLane(
  reply: ThreadsReply,
  replyById: Map<string, ThreadsReply>,
  accountUsername?: string
): ReplyLane {
  if (isOwnReply(reply, accountUsername)) {
    return "mine";
  }
  const parentId = parentReplyId(reply);
  const parent = parentId ? replyById.get(parentId) : undefined;
  if (parent && isOwnReply(parent, accountUsername)) {
    return "asking_me";
  }
  return "visitor";
}

export function needsMyReply(
  reply: ThreadsReply,
  allReplies: ThreadsReply[],
  accountUsername?: string
): boolean {
  if (isOwnReply(reply, accountUsername)) {
    return false;
  }
  return !hasOwnReplyTo(allReplies, reply.id, accountUsername);
}

export function threadHasPending(root: ReplyTreeNode, allReplies: ThreadsReply[], accountUsername?: string): boolean {
  const walk = (node: ReplyTreeNode): boolean => {
    if (needsMyReply(node, allReplies, accountUsername)) {
      return true;
    }
    return node.children.some(walk);
  };
  return walk(root);
}

export function countPendingThreads(roots: ReplyTreeNode[], allReplies: ThreadsReply[], accountUsername?: string): number {
  return roots.filter((root) => threadHasPending(root, allReplies, accountUsername)).length;
}

/** Short label for who this reply is directed at (empty for direct comments on the post). */
export function formatReplyRelation(
  reply: ThreadsReply,
  postMediaId: string,
  replyById: Map<string, ThreadsReply>,
  accountUsername?: string
): string {
  const parentId = parentReplyId(reply);
  const rootId = postMediaId.trim();
  if (!parentId || parentId === rootId) {
    return "";
  }
  const target = describeReplyTarget(reply, postMediaId, "", replyById, accountUsername);
  if (target.kind === "me") return "回覆你";
  if (target.kind === "user") return target.label;
  if (target.kind === "unknown") return target.label;
  return "回覆上一則";
}

/** Depth-first thread walk: oldest → newest within each branch. */
export function flattenThread(root: ReplyTreeNode): ReplyTreeNode[] {
  const out: ReplyTreeNode[] = [];
  const walk = (node: ReplyTreeNode) => {
    out.push(node);
    for (const child of node.children) {
      walk(child);
    }
  };
  walk(root);
  return out;
}