/**
 * Social Engine — Phase 6: Distributor
 * Posts approved content to Twitter/X via existing API service.
 * Respects shared 30 tweets/day limit with twitter-agent.worker.
 */
import { postTweet } from '../../twitter/services/twitter-api.service';
import { getTweetCountLast24Hours } from '../../twitter/twitter-data-queries';
import {
  expireStaleApprovedContent,
  getApprovedContent,
  getSocialPostCountLastHours,
  updateContentStatus,
  getContentThreadParts,
} from './data-queries';
import { isAllowedAmericanContent } from './sport-policy';

const MAX_TWEETS_24H = parseInt(process.env.RAINMAKER_MAX_TWEETS_24H || '30');
const MAX_PER_CYCLE = 1;
const MAX_POSTS_PER_HOUR = parseInt(process.env.RAINMAKER_SOCIAL_MAX_POSTS_PER_HOUR || '1');
const MAX_CANDIDATE_MULTIPLIER = 5;
const MIN_CANDIDATE_SCAN = 20;
const INTER_TWEET_DELAY_MS = 3000;
const CREDITS_DEPLETED_BACKOFF_HOURS = parseInt(process.env.RAINMAKER_TWITTER_402_BACKOFF_HOURS || '6');
const MAX_CONTENT_AGE_HOURS = parseInt(process.env.RAINMAKER_SOCIAL_MAX_CONTENT_AGE_HOURS || '12');

function isCreditsDepletedError(err: unknown): boolean {
  const message = err instanceof Error ? err.message : String(err || '');
  return message.includes('CreditsDepleted') || message.includes('failed 402');
}

async function deferContentForCreditsDepleted(content: any, remainingApproved: any[]): Promise<void> {
  const deferredUntil = new Date(Date.now() + CREDITS_DEPLETED_BACKOFF_HOURS * 60 * 60 * 1000);
  const deferredReason = 'twitter_credits_depleted';

  const deferOne = async (piece: any) => {
    await updateContentStatus(piece.id, 'approved', {
      source_data: {
        ...(piece.source_data || {}),
        distribution_hold_until: deferredUntil.toISOString(),
        distribution_hold_reason: deferredReason,
        last_distribution_error: 'CreditsDepleted',
      },
    });
  };

  await deferOne(content);
  for (const piece of remainingApproved) {
    await deferOne(piece);
  }

  console.warn(
    `[social-engine] Twitter credits depleted. Deferred ${1 + remainingApproved.length} approved items until ${deferredUntil.toISOString()}`,
  );
}

export async function distributeContent(): Promise<number> {
  console.log('[social-engine] Phase 6: Distributing approved content...');

  const staleMarked = await expireStaleApprovedContent(MAX_CONTENT_AGE_HOURS);
  if (staleMarked > 0) {
    console.log(`[social-engine] Marked ${staleMarked} stale approved content item(s) as failed`);
  }

  const socialPostedLastHour = await getSocialPostCountLastHours(1);
  if (socialPostedLastHour >= MAX_POSTS_PER_HOUR) {
    console.log(
      `[social-engine] Social hourly cap reached (${socialPostedLastHour}/${MAX_POSTS_PER_HOUR}), skipping distribution`,
    );
    return 0;
  }

  // Check shared tweet budget
  const tweetsToday = await getTweetCountLast24Hours();
  const remaining = MAX_TWEETS_24H - tweetsToday;

  if (remaining <= 0) {
    console.log(`[social-engine] 24h tweet limit reached (${tweetsToday}/${MAX_TWEETS_24H}), skipping distribution`);
    return 0;
  }

  // Reserve some budget for the existing twitter-agent.worker
  const hourlyRemaining = MAX_POSTS_PER_HOUR - socialPostedLastHour;
  const socialBudget = Math.min(MAX_PER_CYCLE, Math.floor(remaining * 0.4), hourlyRemaining);
  if (socialBudget <= 0) {
    console.log('[social-engine] Insufficient tweet budget for social engine');
    return 0;
  }

  const candidateLimit = Math.max(MIN_CANDIDATE_SCAN, socialBudget * MAX_CANDIDATE_MULTIPLIER);
  const approved = await getApprovedContent(candidateLimit, MAX_CONTENT_AGE_HOURS);
  if (approved.length === 0) {
    console.log('[social-engine] No approved content to distribute');
    return 0;
  }

  let posted = 0;

  for (let index = 0; index < approved.length; index++) {
    const content = approved[index];
    if (!isAllowedAmericanContent(content)) {
      console.log(`[social-engine] Skipping non-American sports content ${content.id} (${content.league || content.sport || 'unknown'})`);
      await updateContentStatus(content.id, 'failed', {
        source_data: {
          ...(content.source_data || {}),
          distribution_skip_reason: 'non_american_sport',
        },
      });
      continue;
    }

    try {
      let tweetId: string;

      if (content.format === 'thread_head') {
        // Post head tweet first, then thread replies
        tweetId = await postThreadContent(content);
        if (tweetId.startsWith('dry_')) {
          await updateContentStatus(content.id, 'preview', {
            tweet_id: tweetId,
            posted_at: new Date(),
          });
          continue;
        }
      } else if (content.format === 'thread_reply') {
        // Skip thread replies here — they're handled by postThreadContent
        continue;
      } else {
        // Single tweet
        const result = await postTweet(content.text);
        tweetId = result.tweetId;
      }

      await updateContentStatus(content.id, tweetId.startsWith('dry_') ? 'preview' : 'posted', {
        tweet_id: tweetId,
        posted_at: new Date(),
      });

      console.log(
        `[social-engine] Posted ${content.persona_slug}: "${content.text.substring(0, 60)}..." → ${tweetId} (heat=${content.trend_heat_score || 0}, engagement=${content.trend_engagement_score || 0})`,
      );
      posted++;

      if (posted >= socialBudget) {
        break;
      }

      // Inter-tweet delay
      if (posted < socialBudget && index < approved.length - 1) {
        await new Promise(r => setTimeout(r, INTER_TWEET_DELAY_MS));
      }
    } catch (err: any) {
      console.error(`[social-engine] Post failed for content ${content.id}:`, err.message);
      if (isCreditsDepletedError(err)) {
        await deferContentForCreditsDepleted(content, approved.slice(index + 1));
        break;
      }
      await updateContentStatus(content.id, 'failed');
    }
  }

  console.log(`[social-engine] Phase 6 complete: ${posted} tweets posted`);
  return posted;
}

async function postThreadContent(headContent: any): Promise<string> {
  // Post head tweet
  const headResult = await postTweet(headContent.text);
  const headTweetId = headResult.tweetId;

  // Get thread replies
  const threadParts = await getContentThreadParts(headContent.id);

  let lastTweetId = headTweetId;
  for (const part of threadParts) {
    try {
      await new Promise(r => setTimeout(r, INTER_TWEET_DELAY_MS));

      const replyResult = await postTweet(part.text, lastTweetId);
      lastTweetId = replyResult.tweetId;

      await updateContentStatus(part.id, replyResult.tweetId.startsWith('dry_') ? 'preview' : 'posted', {
        tweet_id: replyResult.tweetId,
        posted_at: new Date(),
      });
    } catch (err: any) {
      console.error(`[social-engine] Thread reply failed:`, err.message);
      break;
    }
  }

  return headTweetId;
}
