#!/usr/bin/env bun
import { chromium } from 'playwright';

const AUTH_TOKEN = process.env.TWITTER_AUTH_TOKEN;
const CT0 = process.env.TWITTER_CT0;

function validateCookies() {
  if (!AUTH_TOKEN || !CT0) {
    console.error('Error: TWITTER_AUTH_TOKEN and TWITTER_CT0 environment variables are required.');
    console.error('\nGet them from Chrome DevTools → Application → Cookies → https://x.com');
    console.error('Then add them in Zo Settings > Developers as secrets.');
    process.exit(1);
  }
}

const cookies = [
  {
    name: 'auth_token',
    value: AUTH_TOKEN!,
    domain: '.x.com',
    path: '/',
    httpOnly: true,
    secure: true,
    sameSite: 'None' as const,
    expires: Math.floor(Date.now() / 1000) + 86400 * 365,
  },
  {
    name: 'ct0',
    value: CT0!,
    domain: '.x.com',
    path: '/',
    httpOnly: false,
    secure: true,
    sameSite: 'Lax' as const,
    expires: Math.floor(Date.now() / 1000) + 86400 * 365,
  },
];

async function createBrowser() {
  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    viewport: { width: 1280, height: 900 }
  });
  
  await context.addCookies(cookies);
  const page = await context.newPage();
  
  return { browser, page };
}

async function extractTweets(page: any, maxCount: number = 10) {
  await page.waitForSelector('article', { timeout: 15000 }).catch(() => {});
  await page.waitForTimeout(1000);
  
  const tweets = await page.locator('article').all();
  const results = [];
  
  for (let i = 0; i < Math.min(maxCount, tweets.length); i++) {
    const tweet = tweets[i];
    
    // Get author handle
    const authorHandle = await tweet.locator('div[data-testid="User-Name"] a[href^="/"]').first().getAttribute('href').catch(() => '');
    const authorName = await tweet.locator('span').filter({ hasText: /^@/ }).first().innerText().catch(() => '');
    
    // Get tweet text
    const tweetText = await tweet.locator('div[data-testid="tweetText"]').innerText().catch(() => '');
    
    // Get timestamp/link
    const timeLink = await tweet.locator('time').first().locator('..').getAttribute('href').catch(() => '');
    
    // Get engagement stats
    const statsText = await tweet.locator('[role="group"]').innerText().catch(() => '');
    
    // Extract tweet ID from URL
    const tweetId = timeLink?.match(/\/status\/(\d+)/)?.[1] || '';
    
    results.push({
      id: tweetId,
      author: authorHandle?.replace('/', '@') || authorName || 'unknown',
      text: tweetText.substring(0, 300),
      url: timeLink ? `https://x.com${timeLink}` : '',
    });
  }
  
  return results;
}

async function collectTweetsWithScrolling(
  page: any,
  limit: number,
  opts?: { maxScrolls?: number; perPassMax?: number; settleMs?: number }
) {
  const maxScrolls = opts?.maxScrolls ?? 25;
  const perPassMax = opts?.perPassMax ?? 100;
  const settleMs = opts?.settleMs ?? 1200;

  const byKey = new Map<string, { id: string; author: string; text: string; url: string }>();

  let lastSize = 0;
  let stagnantPasses = 0;

  for (let s = 0; s < maxScrolls && byKey.size < limit; s++) {
    const batch = await extractTweets(page, perPassMax);
    for (const t of batch) {
      const key = t.id || t.url;
      if (!key) continue;
      if (!byKey.has(key)) byKey.set(key, t);
    }

    if (byKey.size === lastSize) stagnantPasses += 1;
    else stagnantPasses = 0;
    lastSize = byKey.size;

    if (byKey.size >= limit) break;
    if (stagnantPasses >= 3) break;

    await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
    await page.waitForTimeout(settleMs);
  }

  return Array.from(byKey.values()).slice(0, limit);
}

async function commandTimeline(limit: number = 20) {
  validateCookies();
  const { browser, page } = await createBrowser();
  
  await page.goto('https://x.com/home', { waitUntil: 'domcontentloaded' });
  await page.waitForTimeout(2000);
  
  const tweets = await collectTweetsWithScrolling(page, limit);
  
  console.log(`\n📱 Timeline (${tweets.length} tweets)\n`);
  tweets.forEach((t, i) => {
    console.log(`--- ${i + 1}. ${t.author} ---`);
    console.log(t.text);
    console.log(`URL: ${t.url}\n`);
  });
  
  await browser.close();
}

async function commandBookmarks(limit: number = 20) {
  validateCookies();
  const { browser, page } = await createBrowser();
  
  await page.goto('https://x.com/i/bookmarks', { waitUntil: 'domcontentloaded' });
  await page.waitForTimeout(2000);
  
  const tweets = await collectTweetsWithScrolling(page, limit);
  
  console.log(`\n🔖 Bookmarks (${tweets.length} tweets)\n`);
  tweets.forEach((t, i) => {
    console.log(`--- ${i + 1}. ${t.author} ---`);
    console.log(t.text);
    console.log(`URL: ${t.url}\n`);
  });
  
  await browser.close();
}

async function commandNotifications(limit: number = 20) {
  validateCookies();
  const { browser, page } = await createBrowser();
  
  await page.goto('https://x.com/notifications', { waitUntil: 'domcontentloaded' });
  await page.waitForTimeout(2000);
  
  const tweets = await collectTweetsWithScrolling(page, limit);
  
  console.log(`\n🔔 Notifications (${tweets.length})\n`);
  tweets.forEach((t, i) => {
    console.log(`--- ${i + 1}. ${t.author} ---`);
    console.log(t.text);
    console.log(`URL: ${t.url}\n`);
  });
  
  await browser.close();
}

async function commandSearch(query: string, limit: number = 30) {
  validateCookies();
  const { browser, page } = await createBrowser();
  
  const encodedQuery = encodeURIComponent(query);
  await page.goto(`https://x.com/search?q=${encodedQuery}&src=typed_query&f=live`, { waitUntil: 'domcontentloaded' });
  await page.waitForTimeout(2000);
  
  const tweets = await collectTweetsWithScrolling(page, limit);
  
  console.log(`\n🔍 Search: "${query}" (${tweets.length} results)\n`);
  tweets.forEach((t, i) => {
    console.log(`--- ${i + 1}. ${t.author} ---`);
    console.log(t.text);
    console.log(`URL: ${t.url}\n`);
  });
  
  await browser.close();
}

async function commandPost(text: string) {
  validateCookies();
  const { browser, page } = await createBrowser();
  
  await page.goto('https://x.com/home', { waitUntil: 'domcontentloaded' });
  
  // Find the tweet input
  const tweetBox = page.locator('[data-testid="tweetTextarea_0"]');
  await tweetBox.waitFor({ timeout: 10000 });
  
  await tweetBox.fill(text);
  await page.waitForTimeout(500);
  
  // Click the post button
  const postButton = page.locator('[data-testid="tweetButtonInline"]');
  await postButton.click();
  
  await page.waitForTimeout(2000);
  
  console.log(`✅ Posted: "${text}"`);
  
  await browser.close();
}

async function commandReply(tweetId: string, text: string) {
  validateCookies();
  const { browser, page } = await createBrowser();
  
  await page.goto(`https://x.com/i/status/${tweetId}`, { waitUntil: 'domcontentloaded' });
  await page.waitForTimeout(1000);
  
  // Find the reply input
  const replyBox = page.locator('[data-testid="tweetTextarea_0"]');
  await replyBox.waitFor({ timeout: 10000 });
  
  await replyBox.fill(text);
  await page.waitForTimeout(500);
  
  // Click the reply button
  const replyButton = page.locator('[data-testid="tweetButtonInline"]');
  await replyButton.click();
  
  await page.waitForTimeout(2000);
  
  console.log(`✅ Replied to ${tweetId}: "${text}"`);
  
  await browser.close();
}

function printHelp() {
  console.log(`
Twitter/X CLI Tool

Usage: bun scripts/twitter.ts <command> [args]

Commands:
  timeline [limit]                 Get your home timeline (default: 20)
  bookmarks [limit]                Get your bookmarked tweets (default: 20)
  notifications [limit]            Get your notifications (default: 20)
  search <query> [limit]           Search for tweets (default: 30)
  post <text>              Post a new tweet
  reply <tweet-id> <text>  Reply to a tweet

Environment Variables:
  TWITTER_AUTH_TOKEN       Your Twitter auth_token cookie
  TWITTER_CT0              Your Twitter ct0 cookie

Get cookies from Chrome DevTools → Application → Cookies → https://x.com
  `);
}

async function main() {
  const command = process.argv[2];
  
  switch (command) {
    case 'timeline':
      await commandTimeline(Number(process.argv[3] || 20));
      break;
    case 'bookmarks':
      await commandBookmarks(Number(process.argv[3] || 20));
      break;
    case 'notifications':
      await commandNotifications(Number(process.argv[3] || 20));
      break;
    case 'search':
      const query = process.argv[3];
      if (!query) {
        console.error('Error: search requires a query argument');
        process.exit(1);
      }
      await commandSearch(query, Number(process.argv[4] || 30));
      break;
    case 'post':
      const postText = process.argv.slice(3).join(' ');
      if (!postText) {
        console.error('Error: post requires text argument');
        process.exit(1);
      }
      await commandPost(postText);
      break;
    case 'reply':
      const tweetId = process.argv[3];
      const replyText = process.argv.slice(4).join(' ');
      if (!tweetId || !replyText) {
        console.error('Error: reply requires tweet-id and text arguments');
        process.exit(1);
      }
      await commandReply(tweetId, replyText);
      break;
    case '--help':
    case '-h':
      printHelp();
      break;
    default:
      console.error(`Unknown command: ${command}`);
      printHelp();
      process.exit(1);
  }
}

main().catch(e => {
  console.error('Error:', e.message);
  process.exit(1);
});