import { Injectable, Logger } from "@nestjs/common";
import { HttpService } from "@nestjs/axios";
import { firstValueFrom } from "rxjs";
import * as cheerio from "cheerio";

export interface ScrapedMatchDetails {
  homeTeam: string;
  awayTeam: string;
  homeTeamId: string;
  awayTeamId: string;
  league: string;
  scoreHome: number | null;
  scoreAway: number | null;
  status: string;
  date: string;
  mstUtc: number;
}

export interface ScrapedOdds {
  id: string;
  name: string;
  mbc: string;
  selectionCollection: any[];
}

export interface ScrapedData {
  matchDetails: ScrapedMatchDetails;
  odds: ScrapedOdds[];
  eventData: any[];
  stats: { home: any; away: any };
  lineups: { home: any[]; away: any[] };
  substitutes: { home: any[]; away: any[] } | null;
}

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

  constructor(private readonly httpService: HttpService) {}

  /**
   * Main scrape method - fetches all match data from Mackolik
   */
  async scrapeMatchData(
    matchId: string,
    matchSlug: string,
    sport: "football" | "basketball",
  ): Promise<ScrapedData> {
    const sportPath = sport === "basketball" ? "basketbol/mac" : "mac";

    const oddsUrl = `https://www.mackolik.com/${sportPath}/${matchSlug}/iddaa/${matchId}`;
    const infoUrl = `https://www.mackolik.com/${sportPath}/${matchSlug}/${matchId}`;

    try {
      this.logger.log(`Scraping match data for ${matchId}`);

      // Parallel fetch
      const [oddsResponse, infoResponse] = await Promise.all([
        firstValueFrom(this.httpService.get(oddsUrl)),
        firstValueFrom(this.httpService.get(infoUrl)),
      ]);

      // Parse odds page
      const oddsSettings = this.extractDataSettings(oddsResponse.data);
      const odds = this.transformOdds(oddsSettings);

      // Parse info page
      const infoSettings = this.extractDataSettings(infoResponse.data);
      const dataLayer = this.extractDataLayer(infoResponse.data);

      // Extract match details
      const matchDetails = this.extractMatchDetails(infoSettings, dataLayer);

      // Extract events
      const eventData = this.extractKeyEvents(infoSettings);

      // Extract stats
      const stats = this.extractGameStats(infoSettings);

      // Extract lineups
      const lineups = await this.fetchLineups(matchId);

      // Fetch substitutes
      const substitutes = await this.fetchSubstitutes(matchId);

      return {
        matchDetails,
        odds,
        eventData,
        stats,
        lineups,
        substitutes,
      };
    } catch (error: any) {
      this.logger.error(`Scrape failed for ${matchId}: ${error.message}`);
      throw error;
    }
  }

  /**
   * Extract all [data-settings] attributes from HTML
   */
  extractDataSettings(html: string): any[] {
    const $ = cheerio.load(html);
    const settings: any[] = [];

    $("[data-settings]").each((i, elem) => {
      const settingsJson = $(elem).attr("data-settings");
      if (settingsJson) {
        try {
          settings.push(JSON.parse(settingsJson));
        } catch {
          // Ignore parse errors
        }
      }
    });

    return settings;
  }

  /**
   * Extract window.dataLayer from HTML
   */
  extractDataLayer(html: string): any {
    const match = html.match(/window\.dataLayer\s*=\s*(\[[\s\S]*?\]);/);
    if (match && match[1]) {
      try {
        return JSON.parse(match[1])[0];
      } catch {
        return null;
      }
    }
    return null;
  }

  /**
   * Transform odds from data-settings
   */
  private transformOdds(settings: any[]): ScrapedOdds[] {
    const oddsData = settings.find((s) => s.iddaaEventId?.marketCollection);
    if (!oddsData) return [];

    const markets = Object.values(oddsData.iddaaEventId.marketCollection);
    return markets.map((market: any) => ({
      id: market.id,
      name: market.name,
      mbc: market.mbc,
      selectionCollection: Object.values(market.selectionCollection || {}),
    }));
  }

  /**
   * Extract match details from dataLayer
   */
  private extractMatchDetails(
    settings: any[],
    dataLayer: any,
  ): ScrapedMatchDetails {
    const matchInfo = settings.find((s) => s.homeTeamName && s.matchId);
    const matchHeader = settings.find((s) => s.match?.startTime);

    const prf = dataLayer?.prf || {};

    return {
      homeTeam: matchInfo?.homeTeamName || prf.team1Name || "Unknown",
      awayTeam: matchInfo?.awayTeamName || prf.team2Name || "Unknown",
      homeTeamId: prf.team1Id?.toString() || "0",
      awayTeamId: prf.team2Id?.toString() || "0",
      league: prf.competitionName || "Unknown",
      scoreHome: prf.team1Score != null ? parseInt(prf.team1Score) : null,
      scoreAway: prf.team2Score != null ? parseInt(prf.team2Score) : null,
      status: this.extractMatchStatus(settings) || "NS",
      date: matchHeader?.match?.startTime?.utc
        ? new Date(matchHeader.match.startTime.utc).toISOString()
        : new Date().toISOString(),
      mstUtc: matchHeader?.match?.startTime?.utc
        ? new Date(matchHeader.match.startTime.utc).getTime()
        : Date.now(),
    };
  }

  /**
   * Extract match status
   */
  private extractMatchStatus(settings: any[]): string {
    const matchState = settings.find((s) => s.matchState || s.status);
    return matchState?.matchState || matchState?.status || "NS";
  }

  /**
   * Extract key events (goals, cards, etc)
   */
  private extractKeyEvents(settings: any[]): any[] {
    const keyEventsContainer = settings.find(
      (s) => s.keyEvents && Array.isArray(s.keyEvents),
    );
    return keyEventsContainer?.keyEvents || [];
  }

  /**
   * Extract game stats
   */
  private extractGameStats(settings: any[]): { home: any; away: any } {
    const gameStats = settings.find((s) => s.home?.shotsOnTarget !== undefined);
    if (gameStats) {
      return { home: gameStats.home, away: gameStats.away };
    }
    return { home: {}, away: {} };
  }

  /**
   * Fetch lineups from AJAX endpoint (İlk 11)
   */
  async fetchLineups(matchId: string): Promise<{ home: any[]; away: any[] }> {
    try {
      const url = `https://www.mackolik.com/ajax/football/match-stats?matchId=${matchId}&ajaxViewName=starting-formation&seasonId=${matchId}`;
      const response = await firstValueFrom(this.httpService.get(url));

      if (response.data?.data?.home && response.data?.data?.away) {
        return {
          home: response.data.data.home || [],
          away: response.data.data.away || [],
        };
      }
    } catch {
      this.logger.warn(`Could not fetch lineups for ${matchId}`);
    }

    return { home: [], away: [] };
  }

  /**
   * Fetch substitutes
   */
  async fetchSubstitutes(
    matchId: string,
  ): Promise<{ home: any[]; away: any[] } | null> {
    try {
      const url = `https://www.mackolik.com/ajax/football/match-stats?matchId=${matchId}&ajaxViewName=substitutions`;
      const response = await firstValueFrom(this.httpService.get(url));

      if (response.data?.data?.stats) {
        return {
          home: response.data.data.stats.home || [],
          away: response.data.data.stats.away || [],
        };
      }
    } catch {
      this.logger.warn(`Could not fetch substitutes for ${matchId}`);
    }

    return null;
  }

  /**
   * Flatten odds for Python engine
   */
  flattenOdds(odds: ScrapedOdds[]): any[] {
    const result: any[] = [];

    for (const market of odds) {
      for (const selection of market.selectionCollection) {
        result.push({
          category: market.name,
          selection: selection.name,
          odd_value: selection.odd,
        });
      }
    }

    return result;
  }
}
