/**
 * Feeder Transformer Service - Senior Level Implementation
 * Transforms raw API data into database-ready formats
 */

import { Injectable, Logger } from "@nestjs/common";
import * as cheerio from "cheerio";
import {
  RawKeyEvent,
  TransformedEvent,
  RawPlayerStats,
  TransformedPlayer,
  MatchParticipation,
  TransformedMatchStats,
  ParsedMarket,
  MatchOfficial,
  MatchState,
  GameStatsResponse,
  DbEventPayload,
  DbMarketPayload,
} from "./feeder.types";

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

  // ============================================
  // HELPER FUNCTIONS
  // ============================================
  private safeString(value: any): string | null {
    return value === null || value === undefined || value === ""
      ? null
      : String(value);
  }

  private safeInt(value: any): number | null {
    const num = parseInt(String(value), 10);
    return isNaN(num) ? null : num;
  }

  private safeFloat(value: any): number | null {
    const num = parseFloat(String(value));
    return isNaN(num) ? null : num;
  }

  private extractPlayerIdFromUrl(url: string | undefined): string | null {
    if (!url) return null;
    const parts = url.split("/");
    return parts[parts.length - 1] || null;
  }

  // ============================================
  // KEY EVENTS TRANSFORMER
  // ============================================
  transformKeyEvents(
    rawEvents: RawKeyEvent[],
    homeTeamId: string,
    awayTeamId: string,
    matchId: string,
  ): TransformedEvent[] {
    return rawEvents.map((e) => {
      const playerId = this.extractPlayerIdFromUrl(e.playerUrl) || "";
      const assistPlayerId = e.assistPlayerUrl
        ? this.extractPlayerIdFromUrl(e.assistPlayerUrl)
        : null;
      const playerOutId = e.playerOutUrl
        ? this.extractPlayerIdFromUrl(e.playerOutUrl)
        : null;

      // Determine event type
      let eventType: "goal" | "card" | "substitute" | "other" = "other";
      if (e.type === "goal") eventType = "goal";
      else if (e.type === "card") eventType = "card";
      else if (e.type === "substitute") eventType = "substitute";

      return {
        matchId,
        playerId,
        playerName: e.playerName,
        teamId: e.position === "home" ? homeTeamId : awayTeamId,
        eventType,
        eventSubtype: e.subType || null,
        timeMinute: e.timeMin,
        timeSeconds: e.seconds,
        periodId: e.periodId,
        assistPlayerId,
        assistPlayerName: e.assistPlayerName || null,
        scoreAfter: e.score || null,
        playerOutId,
        playerOutName: e.playerOutName || null,
        position: e.position,
      };
    });
  }

  // ============================================
  // LINEUP PROCESSOR
  // ============================================
  processLineup(
    players: RawPlayerStats[],
    teamId: string,
    isStarting: boolean,
    matchId: string,
    playersMap: Map<string, TransformedPlayer>,
    participationData: MatchParticipation[],
  ): void {
    if (!players || !Array.isArray(players)) return;

    players.forEach((p) => {
      const playerId = this.safeString(p.personId);
      const playerName = this.safeString(p.matchName);

      if (playerId && playerName) {
        // Add to players map (for players table insert)
        playersMap.set(playerId, {
          id: playerId,
          name: playerName,
          slug: playerId,
          teamId,
        });

        // Add participation record
        participationData.push({
          matchId,
          playerId,
          teamId,
          position: this.safeString(p.position),
          shirtNumber: this.safeInt(p.shirtNumber),
          isStarting,
        });
      }
    });
  }

  // ============================================
  // GAME STATS TRANSFORMER
  // ============================================
  transformGameStats(
    data: GameStatsResponse["data"] | null,
  ): TransformedMatchStats | null {
    if (!data || !data.home) return null;

    // Away possession can be calculated if not provided
    const awayPossession: number | undefined =
      data.away.possesionPercentage ??
      (data.home.possesionPercentage
        ? 100 - data.home.possesionPercentage
        : undefined);

    return {
      home: {
        possesionPercentage: data.home.possesionPercentage,
        shotsOnTarget: data.home.shotsOnTarget,
        shotsOffTarget: data.home.shotsOffTarget,
        totalPasses: data.home.totalPasses,
        corners: data.home.corners,
        fouls: data.home.fouls,
        offsides: data.home.offsides,
      },
      away: {
        possesionPercentage: awayPossession,
        shotsOnTarget: data.away.shotsOnTarget,
        shotsOffTarget: data.away.shotsOffTarget,
        totalPasses: data.away.totalPasses,
        corners: data.away.corners,
        fouls: data.away.fouls,
        offsides: data.away.offsides,
      },
    };
  }

  // ============================================
  // MATCH STATE TO STATUS MAPPER
  // ============================================
  mapMatchStateToStatus(state: MatchState | undefined): string {
    if (!state) return "NS";

    switch (state) {
      case "postGame":
      case "post":
        return "FT";
      case "preGame":
      case "pre":
        return "NS";
      case "live":
      case "liveGame":
        return "LIVE";
      default:
        return "NS";
    }
  }

  // ============================================
  // OFFICIALS PARSER (from match page HTML)
  // ============================================
  parseOfficials(html: string): MatchOfficial[] {
    if (!html) return [];

    const $ = cheerio.load(html);
    const officials: MatchOfficial[] = [];

    // Try standard officials component
    $(".p0c-match-officials__official-list-item").each((_, elem) => {
      const name = $(elem)
        .find(".p0c-match-officials__official-name")
        .text()
        .trim();
      const role = $(elem)
        .find(".p0c-match-officials__official-group-title")
        .text()
        .trim();

      if (name) {
        officials.push({ name, role: role || "Referee" });
      }
    });

    // Fallback: look for referee info in match info section
    if (officials.length === 0) {
      // Try alternative selectors
      $(".widget-match-info__referee-name, .referee-name").each((_, elem) => {
        const name = $(elem).text().trim();
        if (name) {
          officials.push({ name, role: "Referee" });
        }
      });
    }

    return officials;
  }

  // ============================================
  // IDDAA MARKETS TRANSFORMER
  // For converting ParsedMarket[] to database format
  // ============================================
  transformIddaaMarkets(markets: ParsedMarket[]): DbMarketPayload[] {
    return markets.map((market) => ({
      id: market.marketId,
      name: market.marketName,
      iddaaCode: market.iddaaCode,
      mbc: market.mbc,
      selectionCollection: market.selections.map((s) => ({
        shortcode: s.shortcode,
        name: s.label,
        odd: s.value,
        position: s.outcomeNo,
      })),
    }));
  }

  /**
   * Helper to convert ParsedMarket[] to LiveMatch.odds structure
   * Useful for quick JSON storage
   */
  transformToOddsJson(
    markets: DbMarketPayload[],
  ): Record<string, Record<string, number>> {
    const odds: Record<string, Record<string, number>> = {};
    for (const market of markets) {
      if (!market.name || !market.selectionCollection) continue;

      const marketName = market.name;
      odds[marketName] = {};

      for (const sel of market.selectionCollection) {
        const val = parseFloat(sel.odd);
        if (sel.name && !isNaN(val)) {
          odds[marketName][sel.name] = val;
        }
      }
    }
    return odds;
  }

  // ============================================
  // EXTRACT PLAYERS FROM EVENTS
  // (for adding to players map)
  // ============================================
  extractPlayersFromEvents(
    events: TransformedEvent[],
    playersMap: Map<string, TransformedPlayer>,
  ): void {
    events.forEach((event) => {
      // Main player
      if (
        event.playerId &&
        event.playerName &&
        !playersMap.has(event.playerId)
      ) {
        playersMap.set(event.playerId, {
          id: event.playerId,
          name: event.playerName,
          slug: event.playerId,
        });
      }

      // Assist player
      if (
        event.assistPlayerId &&
        event.assistPlayerName &&
        !playersMap.has(event.assistPlayerId)
      ) {
        playersMap.set(event.assistPlayerId, {
          id: event.assistPlayerId,
          name: event.assistPlayerName,
          slug: event.assistPlayerId,
        });
      }

      // Player out (substitution)
      if (
        event.playerOutId &&
        event.playerOutName &&
        !playersMap.has(event.playerOutId)
      ) {
        playersMap.set(event.playerOutId, {
          id: event.playerOutId,
          name: event.playerOutName,
          slug: event.playerOutId,
        });
      }
    });
  }

  // ============================================
  // PREPARE EVENT DATA FOR DATABASE
  // ============================================
  prepareEventDataForDb(events: TransformedEvent[]): DbEventPayload[] {
    return events
      .filter(
        (
          e,
        ): e is TransformedEvent & {
          eventType: "goal" | "card" | "substitute";
        } => e.eventType !== "other" && !!e.playerId,
      )
      .map((e) => ({
        match_id: e.matchId,
        player_id: e.playerId,
        team_id: e.teamId,
        event_type: e.eventType,
        event_subtype: e.eventSubtype,
        time_minute: e.timeMinute,
        time_seconds: e.timeSeconds,
        period_id: e.periodId,
        assist_player_id: e.assistPlayerId,
        score_after: e.scoreAfter,
        player_out_id: e.playerOutId,
        position: e.position,
      }));
  }

  // ============================================
  // BASKETBALL PLAYER ID GENERATOR
  // ============================================
  generateBasketballPlayerId(teamId: string, playerName: string): string {
    return `${teamId}-${playerName.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase()}`;
  }
}
