import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import {
  FxTweetResponse,
  ParsedTweet,
  TweetPreviewResponse,
} from './dto/x-twitter.dto';

/**
 * X/Twitter tweet çekme ve video pipeline'ına dönüştürme servisi.
 *
 * Neden FXTwitter API?
 * - Tamamen ücretsiz, API key gerektirmez
 * - Rate limit yeterli (shorts üretimi için ideal)
 * - Tweet text, media, metrics, thread bilgisi döner
 * - api.fxtwitter.com/{username}/status/{id} formatında çalışır
 *
 * İleride Xquik API'ye yükseltilebilir ($20/ay, 22 tool).
 */
@Injectable()
export class XTwitterService {
  private readonly logger = new Logger(XTwitterService.name);
  private readonly fxTwitterBaseUrl = 'https://api.fxtwitter.com';

  /**
   * X/Twitter URL'sinden tweet ID'sini parse eder.
   * Desteklenen formatlar:
   * - https://x.com/user/status/123
   * - https://twitter.com/user/status/123
   * - https://x.com/i/status/123
   */
  extractTweetId(url: string): { tweetId: string; username: string } {
    const patterns = [
      /(?:x\.com|twitter\.com)\/(\w+)\/status\/(\d+)/,
      /(?:x\.com|twitter\.com)\/i\/status\/(\d+)/,
    ];

    for (const pattern of patterns) {
      const match = url.match(pattern);
      if (match) {
        if (match.length === 3) {
          return { tweetId: match[2], username: match[1] };
        }
        // /i/status/ formatı — username yok
        return { tweetId: match[1], username: 'i' };
      }
    }

    throw new BadRequestException(
      `Geçersiz X/Twitter URL: ${url}. Beklenen format: https://x.com/user/status/123`,
    );
  }

  /**
   * FXTwitter API ile tweet verisini çeker.
   * Thread desteği: Eğer tweet bir thread'in parçasıysa, tüm thread'i toplar.
   */
  async fetchTweet(url: string): Promise<ParsedTweet> {
    const { tweetId, username } = this.extractTweetId(url);

    this.logger.log(`Tweet çekiliyor: @${username}/status/${tweetId}`);

    const apiUrl = `${this.fxTwitterBaseUrl}/${username}/status/${tweetId}`;
    const response = await this.fetchWithRetry(apiUrl);

    if (!response.tweet) {
      throw new BadRequestException(
        `Tweet bulunamadı veya erişilemez: ${tweetId}`,
      );
    }

    const parsed = this.parseFxTweet(response.tweet);

    // Thread tespiti ve toplama
    const thread = await this.collectThread(parsed, username);
    if (thread.length > 1) {
      parsed.isThread = true;
      parsed.threadTweets = thread;
      this.logger.log(
        `Thread tespit edildi: ${thread.length} tweet — @${username}`,
      );
    }

    return parsed;
  }

  /**
   * Tweet verisini video üretimi için ön izleme formatına dönüştürür.
   * Viral skor hesaplar, süre tahmin eder, başlık önerir.
   */
  async previewTweet(url: string): Promise<TweetPreviewResponse> {
    const tweet = await this.fetchTweet(url);

    const viralScore = this.calculateViralScore(tweet);
    const contentType = tweet.isThread
      ? 'thread'
      : tweet.quotedTweet
        ? 'quote_tweet'
        : 'tweet';

    // İçerik uzunluğuna göre süre tahmin et
    const totalText = tweet.isThread
      ? tweet.threadTweets!.map((t) => t.text).join(' ')
      : tweet.text;
    const wordCount = totalText.split(/\s+/).length;
    const estimatedDuration = Math.min(
      Math.max(Math.ceil((wordCount / 2.5) + 5), 15), // Min 15sn, ~2.5 kelime/sn okuma
      90, // Max 90sn
    );

    const suggestedTitle = this.generateTitle(tweet);
    const suggestedPrompt = this.tweetToPrompt(tweet);

    return {
      tweet,
      suggestedTitle,
      suggestedPrompt,
      viralScore,
      contentType,
      estimatedDuration,
    };
  }

  /**
   * Tweet verisini AI prompt formatına dönüştürür.
   * VideoAiService'in anlayacağı zenginleştirilmiş prompt.
   */
  tweetToPrompt(tweet: ParsedTweet): string {
    const parts: string[] = [];

    // Tweet kaynağı ve bağlam
    parts.push(`[X/Twitter Kaynak İçerik]`);
    parts.push(`Yazar: @${tweet.author.username} (${tweet.author.name})`);
    parts.push(
      `Etkileşim: ${this.formatNumber(tweet.metrics.likes)} beğeni, ${this.formatNumber(tweet.metrics.retweets)} retweet, ${this.formatNumber(tweet.metrics.views)} görüntülenme`,
    );

    // Thread içeriği
    if (tweet.isThread && tweet.threadTweets) {
      parts.push(`\nTweet Thread (${tweet.threadTweets.length} tweet):`);
      tweet.threadTweets.forEach((t, i) => {
        parts.push(`${i + 1}. ${t.text}`);
      });
    } else {
      parts.push(`\nTweet İçeriği:\n${tweet.text}`);
    }

    // Quote tweet varsa
    if (tweet.quotedTweet) {
      parts.push(
        `\nAlıntılanan Tweet (@${tweet.quotedTweet.author.username}):\n${tweet.quotedTweet.text}`,
      );
    }

    // Medya bilgisi
    if (tweet.media.length > 0) {
      const photoCount = tweet.media.filter((m) => m.type === 'photo').length;
      const videoCount = tweet.media.filter((m) => m.type === 'video').length;
      const pieces: string[] = [];
      if (photoCount > 0) pieces.push(`${photoCount} fotoğraf`);
      if (videoCount > 0) pieces.push(`${videoCount} video`);
      parts.push(`\nMedya: ${pieces.join(', ')}`);

      // Fotoğraf URL'leri referans olarak
      const photos = tweet.media.filter((m) => m.type === 'photo');
      if (photos.length > 0) {
        parts.push(`Referans Görseller (senaryoda kullanılabilir):`);
        photos.forEach((p, i) => {
          parts.push(`  Görsel ${i + 1}: ${p.url}`);
        });
      }
    }

    // Video üretim talimatı
    parts.push(
      `\nBu tweet içeriğinden etkileyici bir shorts videosu senaryosu oluştur. Tweet'in viral olma nedenlerini analiz et ve izleyiciyi yakalayan bir anlatım kur. Tweet'teki görselleri referans olarak kullan, ayrıca AI ile yeni görseller de üret.`,
    );

    return parts.join('\n');
  }

  /**
   * Thread tweet'lerini toplar.
   * FXTwitter'da direkt thread endpoint yok → author'un son tweet'lerinden thread'i tahmin et.
   */
  private async collectThread(
    rootTweet: ParsedTweet,
    username: string,
  ): Promise<ParsedTweet[]> {
    const threadTweets: ParsedTweet[] = [rootTweet];

    // Tweet'in reply olup olmadığını kontrol et
    // FXTwitter sınırlı thread bilgisi verir, basit heuristik kullanıyoruz
    try {
      // Username'in son birkaç tweet'ini çekmek yerine, sadece mevcut tweet'i
      // thread olarak işaretle (FXTwitter thread_extractor yok)
      // İleride Xquik thread_extractor ile genişletilebilir

      // Şu an: Eğer tweet uzunsa (280+ karakter) ve satır sonları varsa, thread benzeri
      const lines = rootTweet.text.split('\n').filter((l) => l.trim().length > 0);
      if (lines.length >= 3) {
        // Uzun tek tweet — thread gibi ele alınabilir
        return threadTweets;
      }

      return threadTweets;
    } catch (error) {
      this.logger.warn(
        `Thread toplama hatası: ${error instanceof Error ? error.message : 'Bilinmeyen'}`,
      );
      return threadTweets;
    }
  }

  /**
   * FXTwitter API response'unu ParsedTweet'e dönüştürür.
   */
  private parseFxTweet(raw: NonNullable<FxTweetResponse['tweet']>): ParsedTweet {
    const views = raw.views || 1;
    const engagement = raw.likes + raw.retweets + raw.replies;

    const parsed: ParsedTweet = {
      id: raw.id,
      url: raw.url,
      text: raw.text,
      createdAt: raw.created_at,
      author: {
        id: raw.author.id,
        name: raw.author.name,
        username: raw.author.screen_name,
        avatarUrl: raw.author.avatar_url,
        followersCount: raw.author.followers_count,
        verified: raw.author.verified || false,
      },
      metrics: {
        replies: raw.replies,
        retweets: raw.retweets,
        likes: raw.likes,
        views,
        engagementRate: views > 0 ? Number(((engagement / views) * 100).toFixed(2)) : 0,
      },
      media: (raw.media?.all || []).map((m) => ({
        type: m.type,
        url: m.url,
        thumbnailUrl: m.thumbnail_url,
        width: m.width,
        height: m.height,
      })),
      isThread: false,
    };

    // Quote tweet
    if (raw.quote) {
      parsed.quotedTweet = this.parseFxTweet(raw.quote);
    }

    return parsed;
  }

  /**
   * Viral skor hesaplama (0-100).
   * Engagement rate, takipçi oranı ve toplam etkileşim bazlı.
   */
  private calculateViralScore(tweet: ParsedTweet): number {
    const { metrics, author } = tweet;
    let score = 0;

    // Engagement rate katkısı (max 40 puan)
    if (metrics.engagementRate >= 10) score += 40;
    else if (metrics.engagementRate >= 5) score += 30;
    else if (metrics.engagementRate >= 2) score += 20;
    else if (metrics.engagementRate >= 0.5) score += 10;

    // Toplam beğeni katkısı (max 30 puan)
    if (metrics.likes >= 100000) score += 30;
    else if (metrics.likes >= 10000) score += 25;
    else if (metrics.likes >= 1000) score += 15;
    else if (metrics.likes >= 100) score += 8;

    // Görüntülenme katkısı (max 20 puan)
    if (metrics.views >= 10000000) score += 20;
    else if (metrics.views >= 1000000) score += 15;
    else if (metrics.views >= 100000) score += 10;
    else if (metrics.views >= 10000) score += 5;

    // Medya bonus (max 10 puan)
    if (tweet.media.length > 0) score += 5;
    if (tweet.isThread) score += 5;

    return Math.min(score, 100);
  }

  /**
   * Tweet'ten otomatik başlık önerisi üretir.
   */
  private generateTitle(tweet: ParsedTweet): string {
    const text = tweet.text;

    // İlk cümle veya satırı al
    const firstLine = text.split(/[\n.!?]/)[0]?.trim();

    if (firstLine && firstLine.length > 10 && firstLine.length <= 100) {
      return firstLine;
    }

    // Kısa tweet: tamamını kullan
    if (text.length <= 100) {
      return text.replace(/\n/g, ' ').trim();
    }

    // Uzun tweet: ilk 80 karakteri kes
    return text.substring(0, 80).replace(/\n/g, ' ').trim() + '...';
  }

  /**
   * HTTP fetch + retry (FXTwitter bazen yavaş olabiliyor).
   */
  private async fetchWithRetry(
    url: string,
    maxRetries = 3,
  ): Promise<FxTweetResponse> {
    let lastError: Error | null = null;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 15000);

        const res = await fetch(url, {
          method: 'GET',
          headers: {
            'User-Agent': 'ContentGen-AI/1.0',
            Accept: 'application/json',
          },
          signal: controller.signal,
        });

        clearTimeout(timeout);

        if (!res.ok) {
          if (res.status === 429) {
            const retryAfter = res.headers.get('Retry-After');
            const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000;
            this.logger.warn(`Rate limited — ${delay}ms bekleniyor (deneme ${attempt}/${maxRetries})`);
            await this.sleep(delay);
            continue;
          }
          throw new Error(`FXTwitter HTTP ${res.status}: ${res.statusText}`);
        }

        const data = (await res.json()) as FxTweetResponse;
        return data;
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error));
        this.logger.warn(
          `FXTwitter fetch hatası (deneme ${attempt}/${maxRetries}): ${lastError.message}`,
        );

        if (attempt < maxRetries) {
          await this.sleep(1000 * attempt);
        }
      }
    }

    throw new BadRequestException(
      `Tweet çekilemedi (${maxRetries} deneme): ${lastError?.message}`,
    );
  }

  /**
   * Sayı formatlama (1000 → 1K, 1000000 → 1M).
   */
  private formatNumber(num: number): string {
    if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`;
    if (num >= 1_000) return `${(num / 1_000).toFixed(1)}K`;
    return num.toString();
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}
