import type { GrokTweetResponse, GrokThreadResponse } from './types';
import type { EnrichedGameData } from '../seo/data-enrichment';

const GROK_API_KEY = process.env.GROK_API_KEY || '';
const GROK_API_URL = 'https://api.x.ai/v1/chat/completions';
const GROK_MODEL = 'grok-4-1-fast-reasoning';

const SYSTEM_PROMPT = `You are the voice of Sports Claw — a data-driven sports betting analyst on Twitter/X.

ALL pick tweets MUST follow this EXACT template structure:

🚨{HEADLINE}🚨
({LEAGUE}-{Day Mon DD} • {Time} ET)

(Contest)
{AWAY TEAM} @ {HOME TEAM}

Current Market (Alert: {MARKET TYPE})
{MARKET LABEL}: {CURRENT LINE}

Sports Claw Forecast
{PROJECTED LINE} projected

How We Got Here - The Math
{BLOG URL if provided, otherwise omit this section}

Follow Sports Claw

Closing Line:
{SHORT CTA / TIMING NOTE}

FORMAT RULES:
- Follow the template above EXACTLY for all pick-type tweets
- HEADLINE values: "TOTAL ALERT", "SPREAD ALERT", "MONEYLINE ALERT", "PLAYER PROP"
- MARKET TYPE values: "TOTAL", "SPREAD", "MONEYLINE", "PROP"
- ALL dates/times are already provided in Eastern Time (ET). Use them EXACTLY as given — do NOT convert or modify times.
- No character limit — tweets can be as long as needed (X Premium supports up to 25K chars)
- No hashtags needed
- Do NOT include any URLs or links in the "text" field. Blog URLs are added separately by the system.
- Use \\n for line breaks in the JSON text field

PICK CONSISTENCY (CRITICAL):
- ALWAYS include "pickSide" in your JSON response: "home", "away", "over", or "under"
- "pickSide" indicates which side you are recommending (home team, away team, over total, under total)
- If the prompt includes an EXISTING POSITION on a game, you MUST stay consistent with that position
- NEVER recommend the opposite side of a pick we have already published — contradictions destroy audience trust

ALWAYS return valid JSON only. No markdown code fences, no commentary — pure JSON.`;

// ── ET Date Formatter ───────────────────────────────────────

/** Convert a raw gameDate (UTC) into a human-readable ET string for Grok prompts.
 *  e.g. "2026-02-27 23:05:00" → "Fri Feb 27 • 6:05PM ET" */
function formatGameDateET(dateStr: string | undefined | null): string {
  if (!dateStr) return 'TBD';
  try {
    const d = new Date(dateStr);
    if (isNaN(d.getTime())) return 'TBD';
    const weekday = d.toLocaleDateString('en-US', { timeZone: 'America/New_York', weekday: 'short' });
    const month = d.toLocaleDateString('en-US', { timeZone: 'America/New_York', month: 'short' });
    const day = d.toLocaleDateString('en-US', { timeZone: 'America/New_York', day: 'numeric' });
    const hour = d.toLocaleTimeString('en-US', { timeZone: 'America/New_York', hour: 'numeric', minute: '2-digit', hour12: true });
    const compactTime = hour.replace(/\s+/g, '');
    return `${weekday} ${month} ${day} \u2022 ${compactTime} ET`;
  } catch {
    return 'TBD';
  }
}

// ── League Display Label Helper ──────────────────────────────

const LEAGUE_LABELS: Record<string, string> = {
  nba: 'NBA',
  nfl: 'NFL',
  nhl: 'NHL',
  mlb: 'MLB',
  ncaab: 'NCAA Men\'s Basketball',
  ncaaf: 'NCAA Men\'s Football',
  wncaab: 'NCAA Women\'s Basketball',
  wncaaf: 'NCAA Women\'s Football',
  wnba: 'WNBA',
  epl: 'EPL',
  mls: 'MLS',
  ufc: 'UFC',
  pga: 'PGA',
  bundesliga: 'Bundesliga',
  laliga: 'La Liga',
  seriea: 'Serie A',
  ligue1: 'Ligue 1',
};

function getLeagueLabel(league: string): string {
  return LEAGUE_LABELS[league.toLowerCase()] || league.toUpperCase();
}

// ── Grok API Call ────────────────────────────────────────────

export async function callGrokForTweet<T = GrokTweetResponse>(
  userPrompt: string,
  retries: number = 2
): Promise<T | null> {
  if (!GROK_API_KEY) {
    console.error('[twitter-prompts] GROK_API_KEY not set');
    return null;
  }

  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      const response = await fetch(GROK_API_URL, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${GROK_API_KEY}`,
        },
        body: JSON.stringify({
          model: GROK_MODEL,
          messages: [
            { role: 'system', content: SYSTEM_PROMPT },
            { role: 'user', content: userPrompt },
          ],
          temperature: 0.8,
          max_tokens: 500,
        }),
        signal: AbortSignal.timeout(30000),
      });

      if (!response.ok) {
        const errText = await response.text();
        console.error(`[twitter-prompts] Grok API error ${response.status} (attempt ${attempt + 1}):`, errText.slice(0, 300));
        if (attempt < retries) continue;
        return null;
      }

      const data: any = await response.json();
      const rawContent = data.choices?.[0]?.message?.content || '';

      let jsonStr = rawContent.trim();
      if (jsonStr.startsWith('```')) {
        jsonStr = jsonStr.replace(/^```(?:json)?\s*\n?/, '').replace(/\n?```\s*$/, '');
      }

      try {
        return JSON.parse(jsonStr) as T;
      } catch {
        // Try regex extraction for { "text": "..." }
        console.warn(`[twitter-prompts] JSON parse failed (attempt ${attempt + 1}), trying repair...`);
        try {
          const textMatch = jsonStr.match(/"text"\s*:\s*"((?:[^"\\]|\\.)*)"/);
          const imageMatch = jsonStr.match(/"imagePrompt"\s*:\s*"((?:[^"\\]|\\.)*)"/);
          const pickSideMatch = jsonStr.match(/"pickSide"\s*:\s*"((?:[^"\\]|\\.)*)"/);
          const gameHomeMatch = jsonStr.match(/"gameHome"\s*:\s*"((?:[^"\\]|\\.)*)"/);
          const gameAwayMatch = jsonStr.match(/"gameAway"\s*:\s*"((?:[^"\\]|\\.)*)"/);
          if (textMatch) {
            const result: any = {
              text: textMatch[1].replace(/\\"/g, '"').replace(/\\n/g, '\n'),
            };
            if (imageMatch) {
              result.imagePrompt = imageMatch[1].replace(/\\"/g, '"').replace(/\\n/g, '\n');
            }
            if (pickSideMatch) {
              result.pickSide = pickSideMatch[1];
            }
            if (gameHomeMatch) {
              result.gameHome = gameHomeMatch[1].replace(/\\"/g, '"');
            }
            if (gameAwayMatch) {
              result.gameAway = gameAwayMatch[1].replace(/\\"/g, '"');
            }
            return result as T;
          }
        } catch { /* repair failed */ }

        if (attempt < retries) continue;
        console.error('[twitter-prompts] All JSON parse attempts failed');
        return null;
      }
    } catch (error) {
      console.error(`[twitter-prompts] Grok call failed (attempt ${attempt + 1}):`, (error as Error).message);
      if (attempt < retries) continue;
      return null;
    }
  }
  return null;
}

// ── Prompt Builders ──────────────────────────────────────────

export function gamePreviewTweetPrompt(enriched: EnrichedGameData, existingPosition?: string): string {
  const projTotal = enriched.homeForm.avgPts + enriched.awayForm.avgPts + enriched.homeForm.avgPtsAllowed + enriched.awayForm.avgPtsAllowed;
  const projectedTotal = Math.round((projTotal / 2) * 2) / 2; // average and round to .5
  const forecast = enriched.modelPick
    ? `Pick: ${enriched.modelPick.pick} (${enriched.modelPick.confidence})`
    : `${projectedTotal} total points projected`;

  const positionConstraint = existingPosition
    ? `\n\nEXISTING POSITION: We already published a "${existingPosition}" pick on this game. You MUST stay consistent — recommend the same side.`
    : '';

  const leagueLabel = getLeagueLabel(enriched.league);

  return `Generate a game preview pick tweet using the standard template. Return JSON: { "text": "...", "imagePrompt": "...", "pickSide": "home|away|over|under" }

"pickSide" = which side you recommend (home team, away team, over total, or under total).

USE THE STANDARD TEMPLATE — fill in data from below:

🚨{SPREAD ALERT or TOTAL ALERT or MONEYLINE ALERT}🚨
(${leagueLabel}-${formatGameDateET(enriched.gameDate)})

(Contest)
${enriched.awayTeamFull} @ ${enriched.homeTeamFull}

Current Market (Alert: {SPREAD or TOTAL or MONEYLINE})
{Market Label}: {current line}

Sports Claw Forecast
{projected line} projected

Follow Sports Claw

Closing Line:
{One sharp action sentence}

DATA TO USE:
GAME: ${enriched.awayTeamFull} @ ${enriched.homeTeamFull} (${enriched.league.toUpperCase()})
DATE: ${formatGameDateET(enriched.gameDate)}
SPREAD: ${enriched.consensus.spread ?? 'N/A'} | TOTAL: ${enriched.consensus.total ?? 'N/A'}
HOME ML: ${enriched.consensus.homeML ?? 'N/A'} | AWAY ML: ${enriched.consensus.awayML ?? 'N/A'}
HOME FORM: ${enriched.homeForm.record} avg ${enriched.homeForm.avgPts}ppg ${enriched.homeForm.streak}
AWAY FORM: ${enriched.awayForm.record} avg ${enriched.awayForm.avgPts}ppg ${enriched.awayForm.streak}
FORECAST: ${forecast}
LINE MOVEMENT: ${enriched.lineMovement.map(m => `${m.market}: ${m.open}→${m.current}`).join(', ') || 'none'}${enriched.piffSummary ? `\n\nPLAYER PROP INTELLIGENCE:\n${enriched.piffSummary}` : ''}${positionConstraint}

If PIFF data suggests strong player unders/overs, consider how that aligns with the game total.

Choose the appropriate HEADLINE based on your pick (SPREAD ALERT, TOTAL ALERT, or MONEYLINE ALERT). imagePrompt: matchup graphic.`;
}

export function propPickTweetPrompt(
  player: string,
  prop: string,
  line: number,
  context: { team?: string; opponent?: string; recentAvg?: number; dvp?: string; league?: string; gameDate?: string; gameTime?: string },
  existingPosition?: string,
  piffLeg?: { name: string; stat: string; line: number; direction: string; tier_label: string; edge: number; prob: number; dvp_tier?: string }
): string {
  const positionConstraint = existingPosition
    ? `\n\nEXISTING POSITION: We already published a "${existingPosition}" pick on this player prop. You MUST stay consistent — recommend the same side.`
    : '';

  const leagueLabel = context.league ? getLeagueLabel(context.league) : null;

  const piffSignal = piffLeg
    ? `\n\nPIFF 3.0 SIGNAL: ${piffLeg.name} ${piffLeg.direction} ${piffLeg.line} ${piffLeg.stat} [${piffLeg.tier_label}] — edge: +${(piffLeg.edge * 100).toFixed(0)}%, prob: ${(piffLeg.prob * 100).toFixed(0)}%${piffLeg.dvp_tier ? `, DVP: ${piffLeg.dvp_tier}` : ''}\nYou MUST follow this direction (${piffLeg.direction}). Do NOT contradict the PIFF model.`
    : '';

  return `Generate a player prop pick tweet using the standard template. Return JSON: { "text": "...", "imagePrompt": "...", "pickSide": "over|under" }

"pickSide" = whether you recommend over or under this line.${piffLeg ? ` PIFF says "${piffLeg.direction}" — you MUST set pickSide to "${piffLeg.direction}".` : ''}

USE THE STANDARD TEMPLATE — fill in data from below:

🚨PLAYER PROP🚨
(${leagueLabel || context.league?.toUpperCase() || 'N/A'}-${context.gameDate ? formatGameDateET(context.gameDate) : 'TBD'})

(Contest)
${context.team || 'TBD'} vs ${context.opponent || 'TBD'}

Current Market (Alert: PROP)
${prop}: ${line}

Sports Claw Forecast
{projected number based on recent avg} projected

Follow Sports Claw

Closing Line:
{One sentence on why over/under}

DATA TO USE:
PLAYER: ${player} (${context.team || 'TBD'})
PROP: ${prop} ${line}
OPPONENT: ${context.opponent || 'TBD'}
RECENT AVG: ${context.recentAvg ?? 'N/A'}
DVP EDGE: ${context.dvp || 'N/A'}
LEAGUE: ${context.league?.toUpperCase() || 'N/A'}
${context.gameDate ? `DATE: ${formatGameDateET(context.gameDate)}` : ''}${piffSignal}${positionConstraint}

imagePrompt: player card style graphic.`;
}

export function lineAlertTweetPrompt(
  moves: Array<{ game: string; market: string; from: number; to: number; sharp?: boolean; steam?: boolean; gameDate?: string }>,
  league: string,
  existingPosition?: string
): string {
  const biggest = moves[0];
  const moveLines = moves.map(m =>
    `${m.game} ${m.market}: ${m.from}→${m.to}${m.steam ? ' STEAM' : ''}${m.sharp ? ' (sharp)' : ''}`
  ).join('\n');

  const positionConstraint = existingPosition
    ? `\n\nEXISTING POSITION: We already published a "${existingPosition}" pick on this game. You MUST stay consistent — recommend the same side or report the movement neutrally.`
    : '';

  const leagueLabel = getLeagueLabel(league);

  return `Generate a line alert pick tweet using the standard template. Return JSON: { "text": "...", "pickSide": "home|away|over|under" }

"pickSide" = which side you lean toward based on the movement (home team, away team, over, or under).

USE THE STANDARD TEMPLATE — fill in data from below:

🚨{SPREAD ALERT or TOTAL ALERT}🚨
(${leagueLabel}-${biggest?.gameDate ? formatGameDateET(biggest.gameDate) : 'TBD'})

(Contest)
[Away] @ [Home]

Current Market (Alert: {SPREAD or TOTAL})
{Market Label}: {current line}

Sports Claw Forecast
{projected number} projected

Follow Sports Claw

Closing Line:
Sharp action moving [direction] in a big way.

DATA TO USE:
LEAGUE: ${league.toUpperCase()}
LINE MOVEMENTS:
${moveLines}${positionConstraint}

Focus on the BIGGEST move. Choose HEADLINE based on market type (SPREAD ALERT or TOTAL ALERT). Text-only, no imagePrompt.`;
}

export function hotTakeTweetPrompt(gamesContext: string, existingPositions?: string, piffContext?: string): string {
  const positionConstraint = existingPositions
    ? `\n\nEXISTING POSITIONS (DO NOT CONTRADICT):\n${existingPositions}\nIf you pick one of these games, you MUST recommend the same side we already published.`
    : '';

  return `Generate a hot take pick tweet using the standard template. Return JSON: { "text": "...", "imagePrompt": "...", "pickSide": "home|away|over|under", "gameHome": "...", "gameAway": "..." }

"pickSide" = which side you recommend (home team, away team, over, or under).
"gameHome" = the home team name you picked.
"gameAway" = the away team name you picked.

USE THE STANDARD TEMPLATE — pick ONE game and fill in:

🚨{SPREAD ALERT or TOTAL ALERT or MONEYLINE ALERT}🚨
({LEAGUE}-{Day Mon DD} • {Time} ET)

(Contest)
{Away} @ {Home}

Current Market (Alert: {SPREAD or TOTAL or MONEYLINE})
{Market Label}: {current line}

Sports Claw Forecast
{projected number} projected

Follow Sports Claw

Closing Line:
{Bold one-sentence take backed by data}

TODAY'S CONTEXT:
${gamesContext}${piffContext ? `\n\nPLAYER PROP INTELLIGENCE:\n${piffContext}\nConsider PIFF insights when forming your take — strong prop signals can indicate team/player edges.` : ''}${positionConstraint}

Pick ONE game. Choose HEADLINE based on your pick type. imagePrompt: data visualization graphic.`;
}

export function recapTweetPrompt(
  game: { home: string; away: string; homeScore: number; awayScore: number; league: string },
  ourPick?: { side: string; result: 'win' | 'loss' | 'push' }
): string {
  const leagueLabel = getLeagueLabel(game.league);

  let pickInstructions = '';
  if (ourPick) {
    const pickHeader = `\nOUR PICK: ${ourPick.side} — ${ourPick.result.toUpperCase()}`;
    if (ourPick.result === 'win') {
      pickInstructions = `${pickHeader}\nWe called it. Reference the pick briefly and celebrate — one sentence max. Example tone: "We had ${ourPick.side} and it cashed."`;
    } else if (ourPick.result === 'loss') {
      pickInstructions = `${pickHeader}\nOwn the loss. One sentence on what the model missed. No excuses — stay accountable.`;
    } else {
      pickInstructions = `${pickHeader}\nNote the push briefly — "push on ${ourPick.side}" — and move on.`;
    }
  }

  return `Generate a post-game recap tweet. Return JSON: { "text": "..." }

RESULT: ${game.away} ${game.awayScore} — ${game.home} ${game.homeScore} (${leagueLabel})${pickInstructions}

Start the tweet with the league label "${leagueLabel}" on the first line. Keep it factual.${ourPick ? ' IMPORTANT: You MUST mention our pick result — this is how we build trust with followers.' : ''} Tease tomorrow's action.`;
}

export function blogPromoTweetPrompt(title: string, excerpt: string, url: string): string {
  return `Generate a tweet promoting this blog post. Return JSON: { "text": "..." }

TITLE: ${title}
EXCERPT: ${excerpt.substring(0, 200)}

Tease the insight, add a compelling hook. Do NOT include any URL or link. End with "Follow Sports Claw for more".`;
}

export function engagementReplyPrompt(targetTweet: string, sportsContext: string): string {
  return `Generate a value-add reply to this tweet. Return JSON: { "text": "..." }

THEIR TWEET: "${targetTweet}"

RELEVANT DATA WE HAVE:
${sportsContext}

Reply with a useful stat/angle that adds to the conversation. Be helpful, not salesy. Subtly establish credibility. Don't directly pitch — just be the smartest reply in the thread.`;
}

export function freeQuestionAnswerPrompt(question: string, sportsData: string): string {
  return `Generate an answer to this user's sports betting question. Return JSON: { "text": "..." }

THEIR QUESTION: "${question}"

DATA FROM OUR MODELS:
${sportsData}

Answer with TEASER-LEVEL data (enough to be helpful, not enough to replace paid). End with a brief mention of SportsClaw or "Follow for more picks" — do NOT include any URLs.
Keep under 280 chars total.`;
}

export function threadPrompt(topic: string, data: string): string {
  return `Generate a tweet thread. Return JSON: { "hook": "...", "parts": ["...", "..."], "imagePrompt": "..." }

TOPIC: ${topic}
DATA:
${data}

"hook" = first tweet that grabs attention (under 280 chars)
"parts" = 2-4 follow-up tweets (each under 280 chars)
"imagePrompt" = optional graphic for the hook tweet

The hook should make people want to read the thread. Last part should be a strong closing take — no URLs or links.`;
}

export function performanceRecapPrompt(stats: {
  recentWins: number;
  recentLosses: number;
  recentPushes: number;
  recentByLeague: Array<{ league: string; wins: number; losses: number; pushes: number }>;
  overallWins: number;
  overallLosses: number;
  overallPushes: number;
  overallTotal: number;
  overallWinPct: number;
  overallByLeague: Array<{ league: string; wins: number; losses: number; pushes: number }>;
}): string {
  const recentRecord = `${stats.recentWins}-${stats.recentLosses}${stats.recentPushes > 0 ? `-${stats.recentPushes}P` : ''}`;
  const overallRecord = `${stats.overallWins}-${stats.overallLosses}${stats.overallPushes > 0 ? `-${stats.overallPushes}P` : ''}`;

  const recentLeagueLines = stats.recentByLeague
    .map(l => `${getLeagueLabel(l.league)}: ${l.wins}-${l.losses}${l.pushes > 0 ? `-${l.pushes}P` : ''}`)
    .join('\n');

  const overallLeagueLines = stats.overallByLeague
    .map(l => `${getLeagueLabel(l.league)}: ${l.wins}-${l.losses}${l.pushes > 0 ? `-${l.pushes}P` : ''}`)
    .join('\n');

  // Find best league (highest win rate with at least 2 graded)
  const bestLeague = stats.overallByLeague
    .filter(l => l.wins + l.losses >= 2)
    .sort((a, b) => (b.wins / (b.wins + b.losses)) - (a.wins / (a.wins + a.losses)))[0];

  const bestLeagueLine = bestLeague
    ? `BEST LEAGUE: ${getLeagueLabel(bestLeague.league)} at ${Math.round((bestLeague.wins / (bestLeague.wins + bestLeague.losses)) * 100)}% (${bestLeague.wins}-${bestLeague.losses})`
    : '';

  return `Generate a daily performance recap tweet. Return JSON: { "text": "..." }

This is NOT a pick tweet — it's a performance summary. Do NOT use the standard pick template.

These are our PUBLISHED picks on X — graded against final scores from completed games.

Write a confident, data-backed freeform tweet summarizing our track record.

REQUIREMENTS:
- Lead with recent record: "Recent: ${recentRecord}"
- Include overall win rate: ${stats.overallWinPct}% across ${stats.overallTotal} graded picks (${overallRecord})
- ${bestLeagueLine ? `Highlight best league: ${bestLeagueLine}` : 'Mention league breakdowns if interesting'}
- End with "Follow Sports Claw"
- Confident, factual tone — let the numbers speak
- No URLs, no hashtags
- Freeform style, not the standard pick template

RECENT RESULTS (${recentRecord}):
${recentLeagueLines || 'No league breakdown'}

OVERALL RECORD (${overallRecord}, ${stats.overallWinPct}%):
${overallLeagueLines || 'No league breakdown'}
${bestLeagueLine}`;
}

export function eventDrivenAlertPrompt(
  type: string,
  details: string,
  gameDate?: string,
  existingPosition?: string,
  league?: string
): string {
  const emoji = type.toLowerCase().includes('steam') ? '🔥' : '🚨';

  const positionConstraint = existingPosition
    ? `\n\nEXISTING POSITION: We already published a "${existingPosition}" pick on this game. Stay consistent — recommend the same side or frame the alert neutrally.`
    : '';

  const leagueLabel = league ? getLeagueLabel(league) : null;

  return `Generate an event-driven alert pick tweet using the standard template. Return JSON: { "text": "...", "pickSide": "home|away|over|under" }

"pickSide" = which side the alert favors (home team, away team, over, or under).

USE THE STANDARD TEMPLATE — fill in data from below:

🚨{SPREAD ALERT or TOTAL ALERT}🚨
(${leagueLabel || 'N/A'}-${gameDate ? formatGameDateET(gameDate) : 'TBD'})

(Contest)
[Away] @ [Home]

Current Market (Alert: {SPREAD or TOTAL})
{Market Label}: {current line}

Sports Claw Forecast
{projected number} projected

Follow Sports Claw

Closing Line:
{One sentence on sharp action direction — mention ${type}}

ALERT TYPE: ${type}
DETAILS:
${details}
${gameDate ? `GAME DATE: ${formatGameDateET(gameDate)}` : ''}${positionConstraint}

Choose HEADLINE based on market type. Text-only, no imagePrompt.`;
}
