/**
 * Feed Scroll Flow — scroll through a Facebook page's feed and collect posts
 * via intercepted GraphQL responses. Supports configurable depth, date cutoff,
 * and incremental collection (skip already-known posts).
 */

import type { BrowserContext } from '../cdp.js';
import type { NetworkInterceptor, GraphQLResponse } from '../network.js';
import type { FbPostData } from '../../../types.js';
import { delay, scrollFeed } from '../human.js';
import { extractPosts } from '../extract/index.js';

function ts(): string {
  return new Date().toISOString();
}

export interface FeedOptions {
  /** Stop after collecting this many posts. Default: 50 */
  maxPosts?: number;
  /** Stop when encountering posts older than this date. */
  sinceDate?: Date;
  /** Stop when hitting an already-known post ID (incremental scraping). */
  knownPostIds?: Set<string>;
}

const DEFAULT_MAX_POSTS = 50;
const MAX_EMPTY_SCROLLS = 3;

/**
 * Scroll through the current page's feed and collect posts.
 * The page should already be loaded (e.g. after collectPageMetadata).
 */
export async function collectPosts(
  ctx: BrowserContext,
  interceptor: NetworkInterceptor,
  opts: FeedOptions = {},
): Promise<FbPostData[]> {
  const maxPosts = opts.maxPosts ?? DEFAULT_MAX_POSTS;
  const sinceDate = opts.sinceDate;
  const knownPostIds = opts.knownPostIds ?? new Set<string>();

  const collected = new Map<string, FbPostData>();
  let emptyScrollCount = 0;
  let hitDateCutoff = false;
  let hitKnownPost = false;

  console.log(`[${ts()}] ▶ feed: starting feed scroll (max ${maxPosts} posts)`);

  // Listener: capture posts from GraphQL responses
  const onGraphQL = (response: GraphQLResponse) => {
    try {
      const posts = extractPosts(response.body);
      if (!posts || posts.length === 0) return;

      for (const post of posts) {
        if (!post.post_id || collected.has(post.post_id)) continue;

        // Check known-post stop condition
        if (knownPostIds.has(post.post_id)) {
          hitKnownPost = true;
          console.log(`[${ts()}]   hit known post ${post.post_id} — stopping incremental`);
          return;
        }

        // Check date cutoff
        if (sinceDate && post.published_at) {
          const postDate = new Date(post.published_at);
          if (postDate < sinceDate) {
            hitDateCutoff = true;
            console.log(`[${ts()}]   hit date cutoff (${post.published_at}) — stopping`);
            return;
          }
        }

        collected.set(post.post_id, post);
      }
    } catch {
      // Extraction failures on unrelated responses — ignore
    }
  };

  interceptor.on('graphql', onGraphQL);

  try {
    while (
      collected.size < maxPosts &&
      emptyScrollCount < MAX_EMPTY_SCROLLS &&
      !hitDateCutoff &&
      !hitKnownPost
    ) {
      const sizeBefore = collected.size;

      await scrollFeed(ctx);
      await delay(1500 + Math.random() * 1500);

      if (collected.size === sizeBefore) {
        emptyScrollCount++;
        console.log(
          `[${ts()}]   no new posts after scroll (${emptyScrollCount}/${MAX_EMPTY_SCROLLS})`,
        );
      } else {
        emptyScrollCount = 0;
        console.log(`[${ts()}]   ${collected.size} posts collected so far`);
      }
    }

    // Try expanding "See more" on posts with truncated text
    await expandTruncatedPosts(ctx);

    const reason =
      collected.size >= maxPosts ? 'maxPosts reached'
      : hitDateCutoff            ? 'date cutoff'
      : hitKnownPost             ? 'known post hit'
      : emptyScrollCount >= MAX_EMPTY_SCROLLS ? 'no new posts'
      : 'unknown';

    console.log(`[${ts()}] ✓ feed: collected ${collected.size} posts (${reason})`);
    return Array.from(collected.values());
  } finally {
    interceptor.off('graphql', onGraphQL);
  }
}

/**
 * Click all visible "See more" links on posts with truncated text,
 * so the full text is captured by our DOM reads or future GraphQL calls.
 */
async function expandTruncatedPosts(ctx: BrowserContext): Promise<void> {
  try {
    const count = await ctx.evaluate(`
      (() => {
        const links = [...document.querySelectorAll('[role="button"]')]
          .filter(el => el.textContent?.trim() === 'See more');
        links.forEach(el => (el as HTMLElement).click());
        return links.length;
      })()
    `) as number;
    if (count > 0) {
      console.log(`[${ts()}]   expanded ${count} "See more" links`);
      await delay(800 + Math.random() * 400);
    }
  } catch {
    // Non-critical — some "See more" buttons may be stale
  }
}
