/**
 * Rainmaker Twitter API Service
 * OAuth 1.0a signed client for Twitter/X API v2
 * Copied from SportsClaw — uses RAINMAKER_TWITTER_* env vars
 */
import crypto from 'crypto';

const DRY_RUN = process.env.TWITTER_DRY_RUN === 'true';

const TWITTER_API_BASE = 'https://api.twitter.com';
const UPLOAD_API_BASE = 'https://upload.twitter.com';

const CONSUMER_KEY = process.env.RAINMAKER_TWITTER_CONSUMER_KEY || '';
const CONSUMER_SECRET = process.env.RAINMAKER_TWITTER_CONSUMER_SECRET || '';
const ACCESS_TOKEN = process.env.RAINMAKER_TWITTER_ACCESS_TOKEN || '';
const ACCESS_TOKEN_SECRET = process.env.RAINMAKER_TWITTER_ACCESS_TOKEN_SECRET || '';
const TWITTER_USER_ID = process.env.RAINMAKER_TWITTER_USER_ID || '';

// ── OAuth 1.0a Signing ──────────────────────────────────────

function percentEncode(str: string): string {
  return encodeURIComponent(str)
    .replace(/!/g, '%21')
    .replace(/\*/g, '%2A')
    .replace(/'/g, '%27')
    .replace(/\(/g, '%28')
    .replace(/\)/g, '%29');
}

function generateNonce(): string {
  return crypto.randomBytes(16).toString('hex');
}

function generateSignature(
  method: string,
  url: string,
  params: Record<string, string>,
  consumerSecret: string,
  tokenSecret: string
): string {
  const sortedKeys = Object.keys(params).sort();
  const paramString = sortedKeys.map((k) => `${percentEncode(k)}=${percentEncode(params[k])}`).join('&');
  const baseString = `${method.toUpperCase()}&${percentEncode(url)}&${percentEncode(paramString)}`;
  const signingKey = `${percentEncode(consumerSecret)}&${percentEncode(tokenSecret)}`;
  return crypto.createHmac('sha1', signingKey).update(baseString).digest('base64');
}

function buildAuthHeader(method: string, url: string, extraParams: Record<string, string> = {}): string {
  const oauthParams: Record<string, string> = {
    oauth_consumer_key: CONSUMER_KEY,
    oauth_nonce: generateNonce(),
    oauth_signature_method: 'HMAC-SHA1',
    oauth_timestamp: Math.floor(Date.now() / 1000).toString(),
    oauth_token: ACCESS_TOKEN,
    oauth_version: '1.0',
  };

  const allParams = { ...oauthParams, ...extraParams };
  const signature = generateSignature(method, url, allParams, CONSUMER_SECRET, ACCESS_TOKEN_SECRET);
  oauthParams['oauth_signature'] = signature;

  const headerParts = Object.keys(oauthParams)
    .sort()
    .map((k) => `${percentEncode(k)}="${percentEncode(oauthParams[k])}"`)
    .join(', ');

  return `OAuth ${headerParts}`;
}

// ── Tweet Posting ───────────────────────────────────────────

export async function postTweet(
  text: string,
  replyToId?: string
): Promise<{ tweetId: string; text: string }> {
  if (DRY_RUN) {
    console.log(`[rm-twitter-api] DRY RUN — would post: "${text.substring(0, 80)}..."`);
    return { tweetId: `dry_${Date.now()}`, text };
  }

  const url = `${TWITTER_API_BASE}/2/tweets`;
  const body: Record<string, unknown> = { text };
  if (replyToId) {
    body.reply = { in_reply_to_tweet_id: replyToId };
  }

  const authHeader = buildAuthHeader('POST', url);
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      Authorization: authHeader,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(30000),
  });

  if (!response.ok) {
    const errText = await response.text();
    throw new Error(`Twitter POST /2/tweets failed ${response.status}: ${errText}`);
  }

  const data: any = await response.json();
  return { tweetId: data.data.id, text: data.data.text };
}

// ── Search & Engagement ─────────────────────────────────────

export async function searchRecentTweets(
  query: string,
  maxResults: number = 25
): Promise<Array<{
  id: string;
  text: string;
  author_id: string;
  created_at: string;
  public_metrics: { like_count: number; retweet_count: number; reply_count: number };
}>> {
  if (DRY_RUN) {
    console.log(`[rm-twitter-api] DRY RUN — would search: "${query}"`);
    return [];
  }

  const url = `${TWITTER_API_BASE}/2/tweets/search/recent`;
  const params = new URLSearchParams({
    query,
    max_results: maxResults.toString(),
    'tweet.fields': 'author_id,created_at,public_metrics',
  });

  const fullUrl = `${url}?${params}`;
  const authHeader = buildAuthHeader('GET', url, Object.fromEntries(params));

  const response = await fetch(fullUrl, {
    headers: { Authorization: authHeader },
    signal: AbortSignal.timeout(15000),
  });

  if (!response.ok) {
    const errText = await response.text();
    throw new Error(`Twitter search failed ${response.status}: ${errText}`);
  }

  const data: any = await response.json();
  return data.data || [];
}

export async function likeTweet(tweetId: string): Promise<void> {
  if (DRY_RUN) {
    console.log(`[rm-twitter-api] DRY RUN — would like tweet ${tweetId}`);
    return;
  }

  const url = `${TWITTER_API_BASE}/2/users/${TWITTER_USER_ID}/likes`;
  const authHeader = buildAuthHeader('POST', url);

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      Authorization: authHeader,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ tweet_id: tweetId }),
    signal: AbortSignal.timeout(15000),
  });

  if (!response.ok) {
    const errText = await response.text();
    throw new Error(`Twitter like failed ${response.status}: ${errText}`);
  }
}

export async function getMentions(
  sinceId?: string
): Promise<Array<{
  id: string;
  text: string;
  author_id: string;
  created_at: string;
}>> {
  if (DRY_RUN) {
    console.log('[rm-twitter-api] DRY RUN — would fetch mentions');
    return [];
  }

  const url = `${TWITTER_API_BASE}/2/users/${TWITTER_USER_ID}/mentions`;
  const params = new URLSearchParams({
    max_results: '20',
    'tweet.fields': 'author_id,created_at',
  });
  if (sinceId) params.set('since_id', sinceId);

  const fullUrl = `${url}?${params}`;
  const authHeader = buildAuthHeader('GET', url, Object.fromEntries(params));

  const response = await fetch(fullUrl, {
    headers: { Authorization: authHeader },
    signal: AbortSignal.timeout(15000),
  });

  if (!response.ok) {
    const errText = await response.text();
    throw new Error(`Twitter mentions failed ${response.status}: ${errText}`);
  }

  const data: any = await response.json();
  return data.data || [];
}

export async function getTweetMetrics(
  tweetIds: string[]
): Promise<Record<string, {
  like_count: number;
  retweet_count: number;
  reply_count: number;
  quote_count?: number;
  impression_count?: number;
  url_link_clicks?: number;
  user_profile_clicks?: number;
}>>
{
  const ids = Array.from(new Set(tweetIds.filter(id => id && !id.startsWith('dry_')))).slice(0, 100);
  if (ids.length === 0 || DRY_RUN) return {};

  const url = `${TWITTER_API_BASE}/2/tweets`;
  const buildParams = (tweetFields: string) => new URLSearchParams({
    ids: ids.join(','),
    'tweet.fields': tweetFields,
  });

  let data: any = null;
  let lastError = '';
  const fieldAttempts = [
    'public_metrics,non_public_metrics,organic_metrics',
    'public_metrics,non_public_metrics',
    'public_metrics',
  ];

  for (const tweetFields of fieldAttempts) {
    const params = buildParams(tweetFields);
    const fullUrl = `${url}?${params}`;
    const authHeader = buildAuthHeader('GET', url, Object.fromEntries(params));
    const response = await fetch(fullUrl, {
      headers: { Authorization: authHeader },
      signal: AbortSignal.timeout(15000),
    });

    if (response.ok) {
      data = await response.json();
      break;
    }

    lastError = await response.text();
  }

  if (!data) {
    throw new Error(`Twitter metrics failed: ${lastError}`);
  }

  const metrics: Record<string, {
    like_count: number;
    retweet_count: number;
    reply_count: number;
    quote_count?: number;
    impression_count?: number;
    url_link_clicks?: number;
    user_profile_clicks?: number;
  }> = {};

  for (const item of data.data || []) {
    const publicMetrics = item.public_metrics || {};
    const nonPublicMetrics = item.non_public_metrics || {};
    const organicMetrics = item.organic_metrics || {};

    metrics[item.id] = {
      like_count: publicMetrics.like_count ?? organicMetrics.like_count ?? 0,
      retweet_count: publicMetrics.retweet_count ?? organicMetrics.retweet_count ?? 0,
      reply_count: publicMetrics.reply_count ?? organicMetrics.reply_count ?? 0,
      quote_count: publicMetrics.quote_count ?? organicMetrics.quote_count ?? 0,
      impression_count: nonPublicMetrics.impression_count ?? organicMetrics.impression_count ?? publicMetrics.impression_count ?? 0,
      url_link_clicks: nonPublicMetrics.url_link_clicks ?? organicMetrics.url_link_clicks ?? 0,
      user_profile_clicks: nonPublicMetrics.user_profile_clicks ?? organicMetrics.user_profile_clicks ?? 0,
    };
  }

  return metrics;
}

// ── Tweet Posting with Media ────────────────────────────────

export async function postTweetWithMedia(
  text: string,
  mediaIds: string[],
  replyToId?: string
): Promise<{ tweetId: string; text: string }> {
  if (DRY_RUN) {
    console.log(`[rm-twitter-api] DRY RUN — would post with media: "${text.substring(0, 80)}..." mediaIds=${mediaIds}`);
    return { tweetId: `dry_${Date.now()}`, text };
  }

  const url = `${TWITTER_API_BASE}/2/tweets`;
  const body: Record<string, unknown> = {
    text,
    media: { media_ids: mediaIds },
  };
  if (replyToId) {
    body.reply = { in_reply_to_tweet_id: replyToId };
  }

  const authHeader = buildAuthHeader('POST', url);
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      Authorization: authHeader,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(30000),
  });

  if (!response.ok) {
    const errText = await response.text();
    throw new Error(`Twitter POST /2/tweets (media) failed ${response.status}: ${errText}`);
  }

  const data: any = await response.json();
  return { tweetId: data.data.id, text: data.data.text };
}
