import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
import { GeminiService } from "../../gemini/gemini.service";
import {
  AiEngineClient,
  AiEngineRequestError,
} from "../../../common/utils/ai-engine-client";
import {
  FrequencyEngineService,
  type MatchCandidate,
} from "./frequency-engine.service";

export type PredictionRiskLevel = "LOW" | "MEDIUM" | "HIGH" | "EXTREME";
export type PredictionDataQuality = "HIGH" | "MEDIUM" | "LOW";
export type BetGrade = "A" | "B" | "C" | "PASS";

export interface PredictionPickRow {
  market: string;
  pick: string;
  probability: number;
  confidence: number;
  odds: number;
  raw_confidence: number;
  calibrated_confidence: number;
  unified_score: number;
  unified_score_label: 'very_reliable' | 'reliable' | 'moderate' | 'low';
  min_required_confidence: number;
  edge: number;
  play_score: number;
  playable: boolean;
  bet_grade: BetGrade;
  stake_units: number;
  decision_reasons: string[];
}

export interface PredictionBetSummaryRow {
  market: string;
  pick: string;
  raw_confidence: number;
  calibrated_confidence: number;
  unified_score: number;
  unified_score_label: 'very_reliable' | 'reliable' | 'moderate' | 'low';
  bet_grade: BetGrade;
  playable: boolean;
  stake_units: number;
  play_score: number;
  reasons: string[];
}

export interface SingleMatchPredictionPackage {
  model_version: string;
  match_info: {
    match_id: string;
    match_name: string;
    home_team: string;
    away_team: string;
    league: string;
    match_date_ms: number;
  };
  data_quality: {
    label: PredictionDataQuality;
    score: number;
    flags: string[];
    home_lineup_count: number;
    away_lineup_count: number;
  };
  risk: {
    level: PredictionRiskLevel;
    score: number;
    is_surprise_risk: boolean;
    surprise_type: string | null;
    warnings: string[];
  };
  engine_breakdown: {
    team: number;
    player: number;
    odds: number;
    referee: number;
  };
  main_pick: PredictionPickRow | null;
  value_pick: PredictionPickRow | null;
  bet_advice: {
    playable: boolean;
    suggested_stake_units: number;
    reason: string;
  };
  bet_summary: PredictionBetSummaryRow[];
  supporting_picks: PredictionPickRow[];
  aggressive_pick: {
    market: string;
    pick: string;
    probability: number;
    confidence: number;
    odds: number | null;
  } | null;
  scenario_top5: Array<{
    score: string;
    prob: number;
    [key: string]: unknown;
  }>;
  score_prediction: {
    ft: string;
    ht: string;
    xg_home: number;
    xg_away: number;
    xg_total: number;
  };
  market_board: Record<string, unknown>;
  reasoning_factors: string[];
  ai_commentary?: string | null;
}

export interface SmartCouponResult {
  strategy: string;
  generated_at: string;
  match_count: number;
  bets: Array<{
    match_id: string;
    match_name: string;
    market: string;
    pick: string;
    probability: number;
    confidence: number;
    odds: number;
    risk_level: PredictionRiskLevel;
    data_quality: PredictionDataQuality;
  }>;
  total_odds: number;
  expected_win_rate: number;
  rejected_matches: Array<{
    match_id: string;
    reason: string;
    threshold?: number;
  }>;
}

@Injectable()
export class SmartCouponService {
  private readonly logger = new Logger(SmartCouponService.name);
  private readonly aiEngineUrl: string;
  private readonly aiEngineClient: AiEngineClient;

  constructor(
    private readonly geminiService: GeminiService,
    private readonly frequencyEngine: FrequencyEngineService,
  ) {
    this.aiEngineUrl = process.env.AI_ENGINE_URL || "http://ai-engine:8000";
    this.aiEngineClient = new AiEngineClient({
      baseUrl: this.aiEngineUrl,
      logger: this.logger,
      serviceName: SmartCouponService.name,
      timeoutMs: 60000,
      maxRetries: 2,
      retryDelayMs: 750,
    });
  }

  async analyzeMatch(matchId: string): Promise<SingleMatchPredictionPackage> {
    let prediction: SingleMatchPredictionPackage;
    try {
      const response =
        await this.aiEngineClient.post<SingleMatchPredictionPackage>(
          `/v20plus/analyze/${matchId}`,
        );
      prediction = response.data;
    } catch (error: unknown) {
      if (error instanceof AiEngineRequestError) {
        const detail =
          typeof error.detail === "string" ? error.detail : error.message;
        throw new HttpException(
          `AI analyze failed: ${detail}`,
          error.status || HttpStatus.SERVICE_UNAVAILABLE,
        );
      }
      throw new HttpException(
        "AI analyze failed",
        HttpStatus.SERVICE_UNAVAILABLE,
      );
    }

    // Generate AI commentary (non-blocking — fail-safe)
    prediction.ai_commentary = await this.generateMatchCommentary(prediction);
    return prediction;
  }

  private async generateMatchCommentary(
    prediction: SingleMatchPredictionPackage,
  ): Promise<string | null> {
    if (!this.geminiService.isAvailable()) {
      return null;
    }

    try {
      const result = await this.geminiService.generateText(
        JSON.stringify(prediction, null, 2),
        {
          model: "gemini-2.0-flash",
          temperature: 0.7,
          maxTokens: 600,
          systemPrompt: MATCH_COMMENTARY_SYSTEM_PROMPT,
        },
      );
      return result.text || null;
    } catch (error) {
      this.logger.warn("AI commentary generation failed, skipping", error);
      return null;
    }
  }

  async generateDailyBankoCoupon(
    matchIds: string[],
  ): Promise<SmartCouponResult | null> {
    if (matchIds.length === 0) {
      return null;
    }

    return this.getSmartCoupon(matchIds, "SAFE", {
      maxMatches: 2,
      minConfidence: 78,
    });
  }

  async getSmartCoupon(
    matchIds: string[],
    strategy:
      | "SAFE"
      | "BALANCED"
      | "AGGRESSIVE"
      | "VALUE"
      | "MIRACLE" = "BALANCED",
    options: { maxMatches?: number; minConfidence?: number } = {},
  ): Promise<SmartCouponResult> {
    try {
      const response = await this.aiEngineClient.post<SmartCouponResult>(
        "/v20plus/coupon",
        {
          match_ids: matchIds,
          strategy,
          max_matches: options.maxMatches,
          min_confidence: options.minConfidence,
        },
      );
      return response.data;
    } catch (error: unknown) {
      this.logger.error("Failed to generate smart coupon", error);
      if (error instanceof AiEngineRequestError) {
        const detail =
          typeof error.detail === "string" ? error.detail : error.message;
        throw new HttpException(
          `Coupon generation failed: ${detail}`,
          error.status || HttpStatus.SERVICE_UNAVAILABLE,
        );
      }
      throw new HttpException(
        "Coupon generation failed",
        HttpStatus.SERVICE_UNAVAILABLE,
      );
    }
  }

  // ─────────────────────────────────────────────────────────────
  // FREQUENCY-BASED COUPON ENGINE
  // ─────────────────────────────────────────────────────────────

  async generateFrequencyBasedCoupon(options: {
    matchIds?: string[];
    maxMatches?: number;
    minSignal?: number;
    markets?: string[];
  }): Promise<FrequencyCouponResult> {
    const maxMatches = options.maxMatches ?? 3;
    const minSignal = options.minSignal ?? 0.7;
    const allowedMarkets = options.markets?.map((m) => m.toUpperCase()) || null;

    this.logger.log(
      `[FrequencyCoupon] Starting — max=${maxMatches}, minSignal=${minSignal}`,
    );

    // 1. Yaklaşan maçları oranlarıyla getir
    const upcomingRows = await this.frequencyEngine.getUpcomingMatchesWithOdds(
      options.matchIds,
      80,
    );

    this.logger.log(
      `[FrequencyCoupon] Found ${upcomingRows.length} upcoming matches with odds`,
    );

    if (upcomingRows.length === 0) {
      return {
        strategy: "FREQUENCY",
        generated_at: new Date().toISOString(),
        bets: [],
        total_odds: 0,
        expected_hit_rate: 0,
        expected_value: 0,
        ev_positive: false,
        reasoning: ["Bültende uygun maç bulunamadı."],
        rejected_matches: [],
      };
    }

    // 2. Her maç için frekans sinyallerini hesapla (paralel)
    const candidatePromises = upcomingRows.map((row) =>
      this.frequencyEngine.buildMatchCandidate(row).then((candidate) => ({
        candidate,
        row,
      })),
    );
    const candidateResults = await Promise.all(candidatePromises);

    // 3. Sinyali olan adayları filtrele
    const allCandidates: Array<{
      candidate: MatchCandidate;
      row: (typeof upcomingRows)[0];
    }> = [];
    const rejected: FrequencyCouponResult["rejected_matches"] = [];

    for (const { candidate, row } of candidateResults) {
      if (!candidate) {
        rejected.push({
          match_id: row.match_id,
          match_name: `${row.home_team_name} vs ${row.away_team_name}`,
          reason: `Yetersiz geçmiş veri (min ${3} maç gerekli)`,
        });
        continue;
      }

      // Market filtresi uygula
      let filteredSignals = candidate.signals;
      if (allowedMarkets) {
        filteredSignals = filteredSignals.filter((s) =>
          allowedMarkets.some((m) => s.market.includes(m)),
        );
      }

      // Min signal filtresi
      filteredSignals = filteredSignals.filter(
        (s) => s.combinedSignal >= minSignal,
      );

      if (filteredSignals.length === 0) {
        rejected.push({
          match_id: row.match_id,
          match_name: `${row.home_team_name} vs ${row.away_team_name}`,
          reason: `Kombinasyon sinyali ${(minSignal * 100).toFixed(0)}% eşiğinin altında`,
        });
        continue;
      }

      // En güçlü sinyali seç
      candidate.signals = filteredSignals;
      candidate.bestSignal = filteredSignals[0];
      allCandidates.push({ candidate, row });
    }

    this.logger.log(
      `[FrequencyCoupon] ${allCandidates.length} candidates passed filters, ${rejected.length} rejected`,
    );

    // 4. En güçlü sinyale göre sırala
    allCandidates.sort(
      (a, b) =>
        (b.candidate.bestSignal?.confidence ?? 0) -
        (a.candidate.bestSignal?.confidence ?? 0),
    );

    // 5. Çeşitlilik: aynı ligden max 2 maç
    const selected: typeof allCandidates = [];
    const leagueCount = new Map<string, number>();

    for (const entry of allCandidates) {
      if (selected.length >= maxMatches) break;

      const lid = entry.candidate.leagueId;
      const currentCount = leagueCount.get(lid) || 0;
      if (currentCount >= 2) {
        rejected.push({
          match_id: entry.candidate.matchId,
          match_name: `${entry.candidate.homeTeamName} vs ${entry.candidate.awayTeamName}`,
          reason: `Aynı ligden zaten 2 maç seçildi (${entry.candidate.leagueName})`,
        });
        continue;
      }

      selected.push(entry);
      leagueCount.set(lid, currentCount + 1);
    }

    // 6. Sonucu oluştur
    const bets: FrequencyCouponResult["bets"] = [];
    let totalOdds = 1;
    let combinedHitRate = 1;
    const reasoning: string[] = [];

    for (const { candidate, row } of selected) {
      const signal = candidate.bestSignal!;
      const betOdds = this.frequencyEngine.getMarketOdds(row, signal.market);

      if (betOdds <= 0) continue;

      const homeBand = this.frequencyEngine.getOddsBand(candidate.homeOdds);
      const awayBand = this.frequencyEngine.getOddsBand(candidate.awayOdds);

      // Lig profili belirle
      let leagueProfile = "NORMAL";
      if (signal.leagueBonus > 0.02) leagueProfile = "GOLCU";
      else if (signal.leagueBonus < -0.02) leagueProfile = "DEFANSIF";

      bets.push({
        match_id: candidate.matchId,
        match_name: `${candidate.homeTeamName} vs ${candidate.awayTeamName}`,
        league: candidate.leagueName,
        market: signal.market,
        pick: signal.pick,
        home_signal: parseFloat(signal.homeSignal.toFixed(3)),
        away_signal: parseFloat(signal.awaySignal.toFixed(3)),
        combined_signal: parseFloat(signal.combinedSignal.toFixed(3)),
        league_profile: leagueProfile,
        historical_hit_rate: parseFloat(signal.combinedSignal.toFixed(3)),
        odds: betOdds,
        home_odds_band: homeBand,
        away_odds_band: awayBand,
        home_match_count: signal.homeMatchCount,
        away_match_count: signal.awayMatchCount,
      });

      totalOdds *= betOdds;
      combinedHitRate *= signal.combinedSignal;

      reasoning.push(
        `${candidate.homeTeamName} vs ${candidate.awayTeamName}: ` +
          `${signal.pick} — Ev(${homeBand}): ${(signal.homeSignal * 100).toFixed(0)}% (${signal.homeMatchCount} maç), ` +
          `Dep(${awayBand}): ${(signal.awaySignal * 100).toFixed(0)}% (${signal.awayMatchCount} maç)`,
      );
    }

    totalOdds = parseFloat(totalOdds.toFixed(2));
    const expectedValue = parseFloat((combinedHitRate * totalOdds).toFixed(3));

    return {
      strategy: "FREQUENCY",
      generated_at: new Date().toISOString(),
      bets,
      total_odds: totalOdds,
      expected_hit_rate: parseFloat(combinedHitRate.toFixed(4)),
      expected_value: expectedValue,
      ev_positive: expectedValue > 1.0,
      reasoning,
      rejected_matches: rejected,
    };
  }
}

// ─────────────────────────────────────────────────────────────
// Frequency Coupon Result Interface
// ─────────────────────────────────────────────────────────────

export interface FrequencyCouponResult {
  strategy: "FREQUENCY";
  generated_at: string;
  bets: Array<{
    match_id: string;
    match_name: string;
    league: string;
    market: string;
    pick: string;
    home_signal: number;
    away_signal: number;
    combined_signal: number;
    league_profile: string;
    historical_hit_rate: number;
    odds: number;
    home_odds_band: string;
    away_odds_band: string;
    home_match_count: number;
    away_match_count: number;
  }>;
  total_odds: number;
  expected_hit_rate: number;
  expected_value: number;
  ev_positive: boolean;
  reasoning: string[];
  rejected_matches: Array<{
    match_id: string;
    match_name: string;
    reason: string;
  }>;
}

const MATCH_COMMENTARY_SYSTEM_PROMPT = `Sen uzman bir futbol bahis analistisin. Sana verilen model çıktısını analiz edip kısa, net ve aksiyon odaklı Türkçe bir yorum yaz.

Kurallar:
- Max 3-4 kısa paragraf, gereksiz uzatma
- Playable olan marketleri ve nedenlerini açıkla
- Edge pozitif olan marketleri vurgula (bahisçiden daha iyi biliyoruz)
- Tüm edge'ler negatifse "trap maç" olarak uyar
- xG ve skor senaryolarına göre strateji öner
- Bahis grade'lerini açıkla: A = güvenli, B = iyi, PASS = oynama
- Data quality ve risk seviyesini yorumla (kadro onaylı mı, probable XI mi)
- "Ben olsam..." formatında kişisel tavsiye ver
- Emoji kullan: ⚽ ✅ ⚠️ 🎯 ❌ 💰
- Markdown formatı KULLANMA, düz metin yaz
- Bahis terminolojisi kullan: edge, value, implied odds, xG`;
