/**
 * Rainmaker Twitter Bot — Engagement Service V2
 *
 * Upgraded targeting:
 * - Smarter search queries (matchup-aware + intent-based)
 * - Higher engagement quality (prioritize tweets with real discussion)
 * - Quote tweet support (graceful fallback if API blocks)
 *
 * Active hours: 8 AM – 11 PM ET, every 30-minute cycle.
 */
import {
  searchRecentTweets,
  likeTweet,
  getMentions,
  postTweet,
} from './twitter-api.service';
import {
  insertEngagement,
  hasEngagedWith,
  getEngagementCountLast24Hours,
} from '../twitter-data-queries';
import pool from '../../db/index';
import { callGrokPersona } from '../../workers/social-engine/persona-prompts';
import { getRecentPersonaMemory } from '../../workers/social-engine/data-queries';
import type { PersonaConfig } from '../../workers/social-engine/types';

const MAX_LIKES_PER_CYCLE = 5;
const MAX_REPLIES_PER_CYCLE = 2;
const MAX_ENGAGEMENT_24H = 150;

const ENGAGEMENT_PERSONAS: Record<string, PersonaConfig> = {
  the_sharp: {
    id: 0,
    slug: 'the_sharp',
    display_name: 'The Sharp',
    voice_style: 'authoritative',
    emoji_prefix: '📊',
    target_audience: 'analytical sports fans',
    content_types: [],
    weight: 1,
    is_active: true,
  },
  the_degen: {
    id: 0,
    slug: 'the_degen',
    display_name: 'The Degen',
    voice_style: 'hype',
    emoji_prefix: '🔥',
    target_audience: 'high-energy sports fans',
    content_types: [],
    weight: 1,
    is_active: true,
  },
  the_educator: {
    id: 0,
    slug: 'the_educator',
    display_name: 'The Educator',
    voice_style: 'explainer',
    emoji_prefix: '🧠',
    target_audience: 'learning-focused sports fans',
    content_types: [],
    weight: 1,
    is_active: true,
  },
  the_culture_host: {
    id: 0,
    slug: 'the_culture_host',
    display_name: 'The Culture Host',
    voice_style: 'debate host',
    emoji_prefix: '🎙️',
    target_audience: 'reply-driven sports fans',
    content_types: [],
    weight: 1,
    is_active: true,
  },
};

// ── Search Queries (V2 — smarter targeting) ─────────────────

async function buildSearchQueries(): Promise<string[]> {
  const queries: string[] = [];

  try {
    const result = await pool.query(`
      SELECT league, home_team, away_team
      FROM rm_forecast_cache
      WHERE starts_at >= NOW()
        AND starts_at <= NOW() + INTERVAL '24 hours'
        AND composite_confidence IS NOT NULL
      ORDER BY composite_confidence DESC
      LIMIT 6
    `);

    if (result.rows.length > 0) {
      // Tier 1: Direct matchup tweets (highest value engagement)
      const matchups = result.rows.slice(0, 4).map(r => `"${r.away_team}" "${r.home_team}"`);
      if (matchups.length > 0) {
        queries.push(`(${matchups.join(' OR ')}) (pick OR forecast OR prediction OR spread OR over OR under) -is:retweet lang:en`);
      }

      // Tier 2: League + analysis keywords
      const leagues = [...new Set(result.rows.map(r => r.league?.toUpperCase()))].filter(Boolean);
      if (leagues.length > 0) {
        queries.push(`(${leagues.join(' OR ')}) (model OR projection OR edge OR analytics OR data) -is:retweet lang:en`);
      }

      // Tier 3: Intent queries (people looking for picks)
      queries.push(`(${leagues.slice(0, 2).join(' OR ')}) ("who should" OR "best pick" OR "any locks" OR "who wins" OR "what do you think") -is:retweet lang:en`);
    }
  } catch (err) {
    console.error('[rm-engagement] Failed to build game queries:', (err as Error).message);
  }

  // Tier 4: Broader community engagement
  queries.push('("sports forecast" OR "sports model" OR "sharp money" OR "closing line value" OR "player props today") -is:retweet lang:en');
  queries.push('("nba picks" OR "nhl picks" OR "ncaab picks") (today OR tonight OR "best bet") -is:retweet lang:en');

  return queries;
}

// ── Main Engagement Cycle (Likes) ───────────────────────────

export async function runEngagementCycle(): Promise<number> {
  let actionsCount = 0;

  try {
    const engagementCount = await getEngagementCountLast24Hours();
    if (engagementCount >= MAX_ENGAGEMENT_24H) {
      console.log(`[rm-engagement] 24h engagement limit reached (${engagementCount}/${MAX_ENGAGEMENT_24H}), skipping`);
      return 0;
    }

    const queries = await buildSearchQueries();
    let likesThisCycle = 0;
    for (const query of queries) {
      if (likesThisCycle >= MAX_LIKES_PER_CYCLE) break;

      try {
        const tweets = await searchRecentTweets(query, 20);
        console.log(`[rm-engagement] Search returned ${tweets.length} tweets for: "${query.substring(0, 60)}..."`);
        if (!tweets.length) continue;

        const now = Date.now();
        const filtered = tweets.filter(t => {
          const age = now - new Date(t.created_at).getTime();
          const ageHours = age / (1000 * 60 * 60);
          // Wider window: 10 min to 12 hours (was 15min-8hr)
          if (ageHours < 0.17 || ageHours > 12) return false;
          // Lower threshold: any engagement or accounts that look real
          if (t.public_metrics.like_count < 1 && t.public_metrics.retweet_count < 1) return false;
          return true;
        });

        // Prioritize tweets with discussion (replies = more visible likes)
        filtered.sort((a, b) =>
          (b.public_metrics.like_count + b.public_metrics.retweet_count * 3 + b.public_metrics.reply_count * 2) -
          (a.public_metrics.like_count + a.public_metrics.retweet_count * 3 + a.public_metrics.reply_count * 2)
        );

        for (const tweet of filtered) {
          if (likesThisCycle >= MAX_LIKES_PER_CYCLE) break;
          if (await hasEngagedWith(tweet.id, 'like')) continue;

          try {
            await likeTweet(tweet.id);
            await insertEngagement({
              actionType: 'like',
              targetTweetId: tweet.id,
              targetUser: tweet.author_id,
            });
            likesThisCycle++;
            actionsCount++;
            console.log(`[rm-engagement] Liked tweet ${tweet.id} (${tweet.public_metrics.like_count} likes): "${tweet.text.substring(0, 60)}..."`);
            await new Promise(r => setTimeout(r, 2000));
          } catch (err) {
            console.error(`[rm-engagement] Like failed for ${tweet.id}:`, (err as Error).message);
          }
        }

        await new Promise(r => setTimeout(r, 1000));
      } catch (err) {
        console.error(`[rm-engagement] Search query failed:`, (err as Error).message);
      }
    }
  } catch (err) {
    console.error('[rm-engagement] Engagement cycle error:', (err as Error).message);
  }

  const total24h = await getEngagementCountLast24Hours();
  console.log(`[rm-engagement] Cycle completed — ${actionsCount} actions this cycle, ${total24h} in last 24h`);
  return actionsCount;
}

// ── Mention Processing (reliable reply surface under current X constraints) ─

export async function processMentions(): Promise<number> {
  let replied = 0;

  try {
    const mentions = await getMentions();
    for (const mention of mentions) {
      if (replied >= MAX_REPLIES_PER_CYCLE) break;
      if (await hasEngagedWith(mention.id, 'mention_reply')) continue;

      const replyText = await buildEngagementReply(mention.text, true);
      if (!replyText) continue;

      try {
        await postTweet(replyText, mention.id);
        await insertEngagement({
          actionType: 'mention_reply',
          targetTweetId: mention.id,
          targetUser: mention.author_id,
          replyText,
        });
        replied++;
        await new Promise(r => setTimeout(r, 3000));
      } catch (err) {
        console.error(`[rm-engagement] Mention reply failed for ${mention.id}:`, (err as Error).message);
      }
    }
  } catch (err) {
    console.error('[rm-engagement] Mention processing failed:', (err as Error).message);
  }

  return replied;
}

function chooseEngagementPersona(text: string): PersonaConfig {
  const lower = text.toLowerCase();
  if (lower.includes('why') || lower.includes('how') || lower.includes('explain')) return ENGAGEMENT_PERSONAS.the_educator;
  if (lower.includes('crazy') || lower.includes('take') || lower.includes('overrated') || lower.includes('underrated')) return ENGAGEMENT_PERSONAS.the_culture_host;
  if (lower.includes('tonight') || lower.includes('best') || lower.includes('pick') || lower.includes('edge')) return ENGAGEMENT_PERSONAS.the_sharp;
  return ENGAGEMENT_PERSONAS.the_degen;
}

async function buildEngagementReply(text: string, isMention: boolean = false): Promise<string | null> {
  const persona = chooseEngagementPersona(text);
  const memory = await getRecentPersonaMemory(persona.id || 0, 4).catch(() => []);
  const memoryBlock = memory.length
    ? `\nRECENT REPLIES/POSTS TO AVOID COPYING:\n${memory.map((item, index) => `${index + 1}. ${item.slice(0, 140)}`).join('\n')}`
    : '';

  const prompt = `Write one short reply in ${persona.display_name}'s voice to this sports post.

POST:
${text}

Rules:
- Reply in 1-2 sentences max
- Sound human and conversational, not like brand copy
- Add one sharp insight, question, or disagreement
- No hashtags
- No URLs
- No mention handles
- Do not sound like customer support
- If the post asks a direct question, answer it with a point of view
- Avoid generic CTA language${memoryBlock}

${isMention ? 'This is a direct mention, so be helpful but still in persona.' : 'This is a proactive engagement reply, so it should be naturally conversation-starting.'}`;

  const result = await callGrokPersona(prompt, persona);
  if (!result?.text) return null;
  return result.text.slice(0, 280).trim();
}
