import { Injectable, Logger, BadRequestException } from "@nestjs/common";
import { PrismaService } from "../database/prisma.service";
import { ScraperService, ScrapedData } from "./scraper.service";
import { AiService, AIPredictionResult } from "./ai.service";

export interface AnalysisResult {
  aiAnalysis: AIPredictionResult;
  strategyUsed: { analysisTactics: any[] };
  similarMatches: any[];
  matchDetails: any;
}

@Injectable()
export class MatchAnalysisService {
  private readonly logger = new Logger(MatchAnalysisService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly scraperService: ScraperService,
    private readonly aiService: AiService,
  ) {}

  /**
   * Main entry point for match analysis
   */
  async analyzeMatch(url: string, userId?: string): Promise<AnalysisResult> {
    this.logger.log(`Starting analysis for: ${url}`);

    // Phase 1: Parse URL
    const { matchId, matchSlug, sport } = this.parseUrl(url);
    if (!matchId) {
      throw new BadRequestException("Invalid match URL");
    }

    // Phase 2: Scrape data with retry
    let scrapedData: ScrapedData;
    const retryDelays = [3000, 5000, 7000];

    for (let attempt = 0; attempt <= retryDelays.length; attempt++) {
      try {
        scrapedData = await this.scraperService.scrapeMatchData(
          matchId,
          matchSlug,
          sport,
        );
        break;
      } catch (error: any) {
        if (error.response?.status === 502 && attempt < retryDelays.length) {
          this.logger.warn(
            `Scraper returned 502. Retrying in ${retryDelays[attempt] / 1000}s...`,
          );
          await new Promise((resolve) =>
            setTimeout(resolve, retryDelays[attempt]),
          );
          continue;
        }
        if (attempt === retryDelays.length) {
          throw new BadRequestException(
            "Maç bilgileri çekilemedi. Lütfen sonra tekrar deneyiniz.",
          );
        }
        throw error;
      }
    }

    this.logger.log("Phase 1 Complete: Data Scraped");

    // Phase 3: Call Python AI Engine
    let pythonAnalysis: AIPredictionResult | null = null;
    try {
      const flatOdds = this.scraperService.flattenOdds(scrapedData!.odds);
      pythonAnalysis = await this.aiService.callPythonEngine(
        { ...scrapedData!.matchDetails, matchId },
        flatOdds,
        scrapedData!.lineups,
        scrapedData!.substitutes,
        scrapedData!.stats,
        scrapedData!.eventData,
      );
    } catch (err: any) {
      this.logger.warn(`Python Engine error: ${err.message}`);
    }

    this.logger.log("Phase 2 Complete: Python Engine Consulted");

    // Phase 4: Generate strategy
    const matchDNA = await this.aggregateDataForAI(
      scrapedData!,
      pythonAnalysis,
    );
    const strategy = this.aiService.getAnalysisStrategy(matchDNA);

    this.logger.log(
      `Phase 3 Complete: Strategy Formulated (${strategy.analysisTactics.length} tactics)`,
    );

    // Phase 5: Find similar matches
    const similarMatches = await this.findSimilarMatches(
      strategy.analysisTactics,
      matchId,
      sport,
    );

    this.logger.log(
      `Phase 4 Complete: Found ${similarMatches.length} similar matches`,
    );

    // Phase 6: Get final AI prediction
    const aiPayload = {
      liveMatchData: {
        matchDetails: scrapedData!.matchDetails,
        odds: this.scraperService.flattenOdds(scrapedData!.odds),
        stats: scrapedData!.stats,
        eventData: scrapedData!.eventData,
        lineups: scrapedData!.lineups,
        pythonEnginePrediction: pythonAnalysis,
      },
      historicalEvidence: similarMatches,
      strategy,
    };

    const aiAnalysis = await this.aiService.getPredictionForMatch(aiPayload);

    this.logger.log("Phase 5 Complete: Analysis Generated");

    // Phase 7: Save to DB if user provided
    if (userId) {
      await this.saveAnalysisResult(userId, matchId, {
        aiAnalysis,
        strategyUsed: strategy,
        similarMatches,
      });
    }

    return {
      aiAnalysis,
      strategyUsed: strategy,
      similarMatches,
      matchDetails: scrapedData!.matchDetails,
    };
  }

  /**
   * Parse Mackolik URL
   */
  private parseUrl(url: string): {
    matchId: string;
    matchSlug: string;
    sport: "football" | "basketball";
  } {
    try {
      const urlObj = new URL(url);
      const pathParts = urlObj.pathname.split("/").filter((p) => p.length > 0);
      let sport: "football" | "basketball" = "football";
      if (pathParts.includes("basketbol")) sport = "basketball";
      const lastPart = pathParts[pathParts.length - 1];
      const slugPart = pathParts[pathParts.length - 2];
      return { matchId: lastPart, matchSlug: slugPart, sport };
    } catch {
      return { matchId: "", matchSlug: "", sport: "football" };
    }
  }

  /**
   * Aggregate data for AI
   */
  private async aggregateDataForAI(
    scrapedData: ScrapedData,
    pythonAnalysis: any,
  ): Promise<any> {
    const flattenedOdds = this.scraperService.flattenOdds(scrapedData.odds);

    // Get comparison data from DB
    const comparisonData = await this.getComparisonData(
      scrapedData.matchDetails.homeTeamId,
      scrapedData.matchDetails.awayTeamId,
    );

    return {
      liveMatchData: {
        matchDetails: scrapedData.matchDetails,
        odds: flattenedOdds,
        stats: scrapedData.stats,
        eventData: scrapedData.eventData,
        lineups: scrapedData.lineups,
        comparisonData,
        pythonEnginePrediction: pythonAnalysis,
      },
      tacticalAnalysis: [],
    };
  }

  /**
   * Get head-to-head and form data
   */
  private async getComparisonData(
    homeTeamId: string,
    awayTeamId: string,
  ): Promise<any> {
    try {
      // H2H matches
      const h2h = await this.prisma.match.findMany({
        where: {
          OR: [
            { homeTeamId, awayTeamId },
            { homeTeamId: awayTeamId, awayTeamId: homeTeamId },
          ],
          state: "Finished",
        },
        orderBy: { mstUtc: "desc" },
        take: 10,
        select: {
          id: true,
          matchName: true,
          scoreHome: true,
          scoreAway: true,
          mstUtc: true,
        },
      });

      // Home team recent form
      const homeForm = await this.prisma.match.findMany({
        where: {
          OR: [{ homeTeamId }, { awayTeamId: homeTeamId }],
          state: "Finished",
        },
        orderBy: { mstUtc: "desc" },
        take: 5,
        select: {
          id: true,
          matchName: true,
          scoreHome: true,
          scoreAway: true,
          homeTeamId: true,
        },
      });

      // Away team recent form
      const awayForm = await this.prisma.match.findMany({
        where: {
          OR: [{ homeTeamId: awayTeamId }, { awayTeamId }],
          state: "Finished",
        },
        orderBy: { mstUtc: "desc" },
        take: 5,
        select: {
          id: true,
          matchName: true,
          scoreHome: true,
          scoreAway: true,
          homeTeamId: true,
        },
      });

      return { h2h, homeForm, awayForm };
    } catch {
      return { h2h: [], homeForm: [], awayForm: [] };
    }
  }

  /**
   * Find similar matches based on tactics
   */
  private async findSimilarMatches(
    tactics: any[],
    currentMatchId: string,
    sport: string,
  ): Promise<any[]> {
    if (!tactics.length) return [];

    try {
      // Get finished matches with similar odds
      const matches = await this.prisma.match.findMany({
        where: {
          sport: sport as any,
          id: { not: currentMatchId },
          scoreHome: { not: null },
        },
        take: 50,
        orderBy: { mstUtc: "desc" },
        select: {
          id: true,
          matchName: true,
          scoreHome: true,
          scoreAway: true,
          mstUtc: true,
          homeTeam: { select: { name: true } },
          awayTeam: { select: { name: true } },
        },
      });

      return matches;
    } catch {
      return [];
    }
  }

  /**
   * Save analysis result
   */
  private async saveAnalysisResult(
    userId: string,
    matchId: string,
    result: any,
  ): Promise<void> {
    try {
      await this.prisma.analysis.create({
        data: {
          userId,
          matchIds: matchId,
          analysisResultJson: result,
        },
      });
    } catch (err: any) {
      this.logger.warn(`Failed to save analysis: ${err.message}`);
    }
  }
}
