import type { EnrichedGameData } from '../seo/data-enrichment';
import type { PickDetails } from './types';
import { buildBreakdownPrompt, callGrokForBreakdown } from './breakdown-prompts';
import { getTodayPostCount, insertBlogPost, postExists } from '../seo/data-queries';
import { insertPick, getRecentPick, updatePickTweetRow } from './breakdown-data-queries';

export interface BreakdownResult {
  blogPostId: number;
  blogUrl: string;
  pickId: number;
  tweetText: string;
}

const BLOG_BASE_URL = 'https://blog.sportsclaw.guru';
const MAX_POSTS_PER_DAY = Number.parseInt(
  process.env.BLOG_MAX_POSTS_PER_DAY || process.env.SEO_MAX_POSTS_PER_DAY || '15',
  10
);

// ── Slug Builder ────────────────────────────────────────────

function buildSlug(pick: PickDetails, enriched: EnrichedGameData): string {
  const normalize = (s: string) =>
    s.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-');

  const rawDate = pick.gameDate || enriched.gameDate || '';
  // Try to get YYYY-MM-DD; if the string doesn't parse to a valid date, fallback to today
  const parsed = new Date(rawDate);
  const datePart = isNaN(parsed.getTime())
    ? new Date().toISOString().slice(0, 10)
    : parsed.toISOString().slice(0, 10);

  if (pick.pickType === 'prop_over' || pick.pickType === 'prop_under') {
    // prop:nba:player:proptype -> "player-proptype-pick-breakdown-date"
    const parts = pick.gameKey.split(':');
    const player = parts[2] || 'player';
    const prop = parts[3] || 'prop';
    return `${normalize(player)}-${normalize(prop)}-pick-breakdown-${datePart}`;
  }

  // Game-level: "away-vs-home-pick-type-breakdown-date"
  return `${normalize(enriched.awayTeamFull)}-vs-${normalize(enriched.homeTeamFull)}-${normalize(pick.pickType)}-breakdown-${datePart}`;
}

// ── Quality Check ───────────────────────────────────────────

function passesBreakdownQuality(content: string, quickFacts: { label: string; value: string }[]): boolean {
  if (content.length < 1500) {
    console.warn(`[breakdown] Content too short: ${content.length} chars (need 1500+)`);
    return false;
  }

  // Check for required sections
  const requiredSections = ['Executive Summary', 'Predicting', 'Inputs', 'Math', 'Change Our Mind', 'Responsible Gaming'];
  const missingSections = requiredSections.filter(s => !content.toLowerCase().includes(s.toLowerCase()));
  if (missingSections.length > 0) {
    console.warn(`[breakdown] Missing sections: ${missingSections.join(', ')}`);
    return false;
  }

  // Check quick_facts has Pick and Line
  const factLabels = quickFacts.map(f => f.label.toLowerCase());
  if (!factLabels.includes('pick') || !factLabels.includes('line')) {
    console.warn('[breakdown] quick_facts missing Pick or Line');
    return false;
  }

  return true;
}

// ── Blog URL Verification ───────────────────────────────────

async function verifyBlogUrl(url: string): Promise<boolean> {
  try {
    const response = await fetch(url, {
      method: 'HEAD',
      signal: AbortSignal.timeout(10000),
      redirect: 'follow',
    });
    return response.ok;
  } catch (err) {
    console.warn(`[breakdown] Blog URL verification failed for ${url}:`, (err as Error).message);
    return false;
  }
}

// ── Tweet Text Builder ──────────────────────────────────────

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

/** Headline + market type + market label mapping by pickType */
function getHeadlineInfo(pickType: string, propName?: string): { headline: string; marketType: string; marketLabel: string } {
  switch (pickType) {
    case 'total':
      return { headline: 'TOTAL ALERT', marketType: 'TOTAL', marketLabel: 'O/U' };
    case 'spread':
      return { headline: 'SPREAD ALERT', marketType: 'SPREAD', marketLabel: 'Spread' };
    case 'moneyline':
      return { headline: 'MONEYLINE ALERT', marketType: 'MONEYLINE', marketLabel: 'ML' };
    case 'prop_over':
    case 'prop_under':
      return { headline: 'PLAYER PROP', marketType: 'PROP', marketLabel: propName || 'Prop' };
    default:
      return { headline: 'PICK ALERT', marketType: pickType.toUpperCase(), marketLabel: pickType };
  }
}

function formatGameDate(dateStr: string): string {
  try {
    const d = new Date(dateStr);
    if (isNaN(d.getTime())) return '';
    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', hour12: true });
    // hour gives e.g. "10 PM" — compact to "10PM"
    const compactTime = hour.replace(/\s+/g, '');
    return `${weekday} ${month} ${day} \u2022 ${compactTime} ET`;
  } catch {
    return '';
  }
}

/** Build the current market line value from enriched consensus or pick data */
function buildCurrentMarketValue(pick: PickDetails, enriched?: import('../seo/data-enrichment').EnrichedGameData): string {
  if (pick.pickType === 'prop_over' || pick.pickType === 'prop_under') {
    return pick.lineValue != null ? String(pick.lineValue) : '';
  }

  if (pick.pickType === 'total' && enriched?.consensus.total != null) {
    return String(enriched.consensus.total);
  }
  if (pick.pickType === 'spread' && enriched?.consensus.spread != null) {
    return String(enriched.consensus.spread);
  }
  if (pick.pickType === 'moneyline') {
    if (enriched?.consensus.homeML != null && enriched?.consensus.awayML != null) {
      return `${enriched.consensus.homeML} / ${enriched.consensus.awayML}`;
    }
  }

  return pick.lineValue != null ? String(pick.lineValue) : '';
}

/**
 * Standard tweet template for ALL pick-type tweets with backlinks.
 * Designed for X Premium (up to 25K chars).
 */
export function generatePickTweetWithBacklink(
  pick: PickDetails,
  blogUrl: string,
  enriched?: import('../seo/data-enrichment').EnrichedGameData,
): string {
  const leagueLabel = LEAGUE_LABELS[pick.league.toLowerCase()] || pick.league.toUpperCase();

  // Derive prop name for prop picks (from gameKey: "prop:league:player:proptype")
  const propName = (pick.pickType === 'prop_over' || pick.pickType === 'prop_under')
    ? pick.gameKey.split(':')[3]?.replace(/-/g, ' ') || 'Prop'
    : undefined;

  const { headline, marketType, marketLabel } = getHeadlineInfo(pick.pickType, propName);

  // Date line
  const dateStr = pick.gameDate || enriched?.gameDate || '';
  const dateLine = dateStr ? formatGameDate(dateStr) : '';

  // Matchup
  const matchupLine = enriched
    ? `${enriched.awayTeamFull} @ ${enriched.homeTeamFull}`
    : '';

  // Current market value
  const currentMarketValue = buildCurrentMarketValue(pick, enriched);

  // Build the tweet
  const lines: string[] = [];

  // Line 1: headline
  lines.push(`\u{1F6A8}${headline}\u{1F6A8}`);

  // Line 2: league + date
  const metaLine = dateLine ? `(${leagueLabel}-${dateLine})` : `(${leagueLabel})`;
  lines.push(metaLine);

  // Blank line + Contest section
  lines.push('');
  lines.push('(Contest)');
  if (matchupLine) lines.push(matchupLine);

  // Blank line + Current Market
  lines.push('');
  const marketValueStr = currentMarketValue ? `: ${currentMarketValue}` : '';
  lines.push(`Current Market (Alert: ${marketType})`);
  lines.push(`${marketLabel}${marketValueStr}`);

  // Blank line + Sports Claw Forecast
  lines.push('');
  lines.push('Sports Claw Forecast');
  lines.push(`${pick.selection} projected`);

  // Blank line + Blog link
  lines.push('');
  lines.push('How We Got Here - The Math');
  lines.push(blogUrl);

  // Blank line + Follow
  lines.push('');
  lines.push('Follow Sports Claw');

  // Blank line + Closing Line
  lines.push('');
  lines.push('Closing Line:');
  lines.push(pick.shortReason);

  return lines.join('\n');
}

// ── Main Orchestrator ───────────────────────────────────────

export async function orchestrateBreakdown(
  enriched: EnrichedGameData,
  pick: PickDetails,
): Promise<BreakdownResult | null> {
  const tag = '[breakdown]';

  if (await getTodayPostCount() >= MAX_POSTS_PER_DAY) {
    console.warn(`${tag} Daily blog cap reached (${MAX_POSTS_PER_DAY}); skipping breakdown publish`);
    return null;
  }

  // 1. Idempotency check — if we already have a pick+blog for this game/type, reuse it
  const existing = await getRecentPick(pick.gameKey, pick.pickType, 48);
  if (existing?.blog_post_id) {
    console.log(`${tag} Reusing existing pick #${existing.id} with blog #${existing.blog_post_id}`);
    const blogUrl = `${BLOG_BASE_URL}/${existing.game_key}`; // slug lookup would be better but this works for reuse
    // Fetch the actual slug from the blog post
    const { pool } = await import('../db/index');
    const slugResult = await pool.query('SELECT slug FROM sc_blog_posts WHERE id = $1', [existing.blog_post_id]);
    const slug = slugResult.rows[0]?.slug;
    if (!slug) {
      console.warn(`${tag} Blog post #${existing.blog_post_id} not found, generating fresh`);
    } else {
      const url = `${BLOG_BASE_URL}/${slug}`;
      return {
        blogPostId: existing.blog_post_id,
        blogUrl: url,
        pickId: existing.id,
        tweetText: generatePickTweetWithBacklink(pick, url, enriched),
      };
    }
  }

  // 2. Generate blog content via Grok
  console.log(`${tag} Generating breakdown for ${pick.gameKey} (${pick.pickType})`);
  const prompt = buildBreakdownPrompt(enriched, pick);
  const grokResult = await callGrokForBreakdown(prompt);

  if (!grokResult) {
    console.error(`${tag} Grok failed to generate breakdown content`);
    return null;
  }

  // 3. Quality check
  if (!passesBreakdownQuality(grokResult.content, grokResult.quick_facts || [])) {
    console.error(`${tag} Breakdown failed quality check`);
    return null;
  }

  // 4. Build slug + check uniqueness
  let slug = buildSlug(pick, enriched);
  if (await postExists(slug)) {
    slug = `${slug}-${Date.now().toString(36)}`;
  }

  if (await getTodayPostCount() >= MAX_POSTS_PER_DAY) {
    console.warn(`${tag} Daily blog cap reached before insert (${MAX_POSTS_PER_DAY}); skipping breakdown publish`);
    return null;
  }

  // 5. Insert blog post
  const gameDate = pick.gameDate
    ? new Date(pick.gameDate)
    : enriched.gameDate
      ? new Date(enriched.gameDate)
      : null;

  const blogPostId = await insertBlogPost({
    slug,
    title: grokResult.title,
    meta_description: grokResult.meta_description,
    h1: grokResult.h1,
    content: grokResult.content,
    excerpt: grokResult.excerpt,
    quick_facts: grokResult.quick_facts,
    faq_schema: grokResult.faq_schema,
    data_tables: grokResult.data_tables,
    sport: pick.league.toLowerCase(),
    content_type: 'pick-breakdown',
    league: pick.league.toLowerCase(),
    game_date: gameDate,
    schema_markup: null,
    status: 'published',
    published_at: new Date(),
  });

  console.log(`${tag} Blog published: id=${blogPostId}, slug=${slug}`);

  // 6. Insert pick record
  const pickId = await insertPick(pick, enriched, blogPostId);
  console.log(`${tag} Pick recorded: id=${pickId}`);

  const blogUrl = `${BLOG_BASE_URL}/${slug}`;

  // 7. Verify blog URL is live (non-blocking — just log if it fails)
  // Next.js ISR means it may take a moment, so we don't block on this
  verifyBlogUrl(blogUrl).then(ok => {
    if (ok) {
      console.log(`${tag} Blog verified live: ${blogUrl}`);
    } else {
      console.warn(`${tag} Blog not yet returning 200 (ISR may need a moment): ${blogUrl}`);
    }
  });

  // 8. Build tweet text with backlink
  const tweetText = generatePickTweetWithBacklink(pick, blogUrl, enriched);
  console.log(`${tag} Generated tweet with backlink (${tweetText.length} chars)`);

  return { blogPostId, blogUrl, pickId, tweetText };
}
