import dotenv from 'dotenv';
dotenv.config({ path: require('path').resolve(__dirname, '../../.env') });

import {
  getUpcomingGames,
  getGameOdds,
  getTeamInjuries,
  getPlayerProps,
  getRecentLineMovements,
  getRecentSharpMoves,
  getCompletedGames,
  getCLVResults,
  getActiveLeagues,
  postExists,
  getTodayPostCount,
  insertBlogPost,
  logJob,
  updateJobStatus,
} from './data-queries';
import {
  gamePreviewPrompt,
  propsAnalysisPrompt,
  lineMovementPrompt,
  weeklyRecapPrompt,
  educationalGuidePrompt,
  GUIDE_TOPICS,
} from './prompts';
import { enrichedGamePreviewPrompt } from './prompts-v2';
import { enrichGameData } from './data-enrichment';
import { queryRAG, isRAGAvailable } from './rag-client';

const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || '';
const CLAUDE_MODEL = process.env.CLAUDE_MODEL || 'claude-sonnet-4-20250514';
const SEO_LLM_GATEWAY_URL = process.env.SEO_LLM_GATEWAY_URL || '';
const SEO_LLM_GATEWAY_MODEL = process.env.SEO_LLM_GATEWAY_MODEL || '';
const SEO_LLM_GATEWAY_MODELS = (process.env.SEO_LLM_GATEWAY_MODELS || SEO_LLM_GATEWAY_MODEL)
  .split(',')
  .map((model) => model.trim())
  .filter(Boolean);
const SEO_LLM_GATEWAY_API_KEY = process.env.SEO_LLM_GATEWAY_API_KEY || '';
const GROK_API_KEY = process.env.GROK_API_KEY || '';
const GROK_API_URL = process.env.GROK_API_URL || 'https://api.x.ai/v1/chat/completions';
const GROK_MODEL = process.env.GROK_MODEL || 'grok-4-1-fast-reasoning';
const CLAUDE_ENABLED = ANTHROPIC_API_KEY.startsWith('sk-ant-api');
const GATEWAY_ENABLED = Boolean(SEO_LLM_GATEWAY_URL && SEO_LLM_GATEWAY_MODELS.length > 0);
const MAX_POSTS_PER_DAY = Number.parseInt(process.env.SEO_MAX_POSTS_PER_DAY || '15', 10);
const MAX_PREVIEWS_PER_DAY = Number.parseInt(process.env.SEO_MAX_PREVIEWS_PER_DAY || '10', 10);  // Reserve slots for other content
const INDEXNOW_KEY = '20a20d88fcfa557ffff5610feb1c416a';

if (ANTHROPIC_API_KEY && !CLAUDE_ENABLED && !GATEWAY_ENABLED) {
  console.warn('[seo-agent] Anthropic OAuth token detected; skipping Claude because this org only allows API-key auth');
}

if (SEO_LLM_GATEWAY_URL && SEO_LLM_GATEWAY_MODELS.length === 0) {
  console.warn('[seo-agent] SEO_LLM_GATEWAY_URL is set without SEO_LLM_GATEWAY_MODEL(S); skipping gateway');
}

async function submitToIndexNow(slug: string): Promise<void> {
  try {
    const url = `https://sportsclaw.guru/blog/${slug}`;
    const res = await fetch('https://api.indexnow.org/indexnow', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        host: 'sportsclaw.guru',
        key: INDEXNOW_KEY,
        keyLocation: `https://sportsclaw.guru/${INDEXNOW_KEY}.txt`,
        urlList: [url],
      }),
      signal: AbortSignal.timeout(10000),
    });
    console.log(`[seo-agent] IndexNow submit ${slug}: ${res.status}`);
  } catch (e) {
    console.warn(`[seo-agent] IndexNow failed for ${slug}:`, (e as Error).message);
  }
}

interface GeneratedPost {
  title: string;
  meta_description: string;
  h1: string;
  excerpt: string;
  quick_facts: { label: string; value: string }[];
  content: string;
  faq_schema: { question: string; answer: string }[];
  data_tables: Record<string, unknown>;
}

type LlmProvider = 'gateway' | 'claude' | 'grok';
type LlmPlan =
  | { provider: 'gateway'; name: string; model: string }
  | { provider: 'claude'; name: string }
  | { provider: 'grok'; name: string };

function slugify(text: string): string {
  return text
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-|-$/g, '')
    .substring(0, 200);
}

function dateSlug(date: Date): string {
  return date.toLocaleDateString('en-CA', { timeZone: 'America/New_York' });
}

async function callLLMRaw(prompt: string, provider: LlmProvider, gatewayModel?: string): Promise<string | null> {
  const systemMsg = 'You are a sports betting content writer. You ALWAYS return valid JSON and nothing else. No markdown code fences, no commentary — pure JSON. Ensure all strings are properly escaped (especially quotes inside HTML content — use &quot; or \\").';

  if (provider === 'gateway') {
    if (!GATEWAY_ENABLED) return null;
    if (!gatewayModel) return null;

    const headers: Record<string, string> = { 'Content-Type': 'application/json' };
    if (SEO_LLM_GATEWAY_API_KEY) {
      headers.Authorization = `Bearer ${SEO_LLM_GATEWAY_API_KEY}`;
    }

    const response = await fetch(SEO_LLM_GATEWAY_URL, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        model: gatewayModel,
        messages: [{ role: 'system', content: systemMsg }, { role: 'user', content: prompt }],
        temperature: 0.7,
        max_tokens: 8000,
      }),
      signal: AbortSignal.timeout(120000),
    });
    if (!response.ok) {
      const errText = await response.text();
      throw new Error(`Gateway API ${response.status}: ${errText.slice(0, 200)}`);
    }
    const data: any = await response.json();
    return data.choices?.[0]?.message?.content || data.content?.[0]?.text || '';
  }

  if (provider === 'claude') {
    if (!CLAUDE_ENABLED) return null;
    const authHeaders: Record<string, string> = { 'x-api-key': ANTHROPIC_API_KEY };

    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', ...authHeaders, 'anthropic-version': '2023-06-01' },
      body: JSON.stringify({ model: CLAUDE_MODEL, system: systemMsg, messages: [{ role: 'user', content: prompt }], temperature: 0.7, max_tokens: 8000 }),
      signal: AbortSignal.timeout(90000),
    });
    if (!response.ok) {
      const errText = await response.text();
      throw new Error(`Claude API ${response.status}: ${errText.slice(0, 200)}`);
    }
    const data: any = await response.json();
    return data.content?.[0]?.text || '';
  } else {
    if (!GROK_API_KEY) return null;
    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: systemMsg }, { role: 'user', content: prompt }], temperature: 0.7, max_tokens: 8000 }),
      signal: AbortSignal.timeout(120000),
    });
    if (!response.ok) {
      const errText = await response.text();
      throw new Error(`Grok API ${response.status}: ${errText.slice(0, 200)}`);
    }
    const data: any = await response.json();
    return data.choices?.[0]?.message?.content || '';
  }
}

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

  try {
    return JSON.parse(jsonStr) as GeneratedPost;
  } catch {
    console.warn('[seo-agent] JSON parse failed, trying repair...');
    try {
      const extractField = (field: string): string => {
        const re = new RegExp(`"${field}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`);
        const match = jsonStr.match(re);
        return match ? match[1].replace(/\\"/g, '"').replace(/\\n/g, '\n') : '';
      };
      const extractArray = (field: string): unknown => {
        const re = new RegExp(`"${field}"\\s*:\\s*(\\[.*?\\])`, 's');
        const match = jsonStr.match(re);
        if (match) try { return JSON.parse(match[1]); } catch { return []; }
        return [];
      };
      const extractObj = (field: string): unknown => {
        const re = new RegExp(`"${field}"\\s*:\\s*(\\{.*?\\})(?=\\s*,\\s*")`, 's');
        const match = jsonStr.match(re);
        if (match) try { return JSON.parse(match[1]); } catch { return {}; }
        return {};
      };

      const contentMatch = jsonStr.match(/"content"\s*:\s*"([\s\S]*?)"\s*,\s*"faq_schema"/);
      const content = contentMatch ? contentMatch[1].replace(/\\"/g, '"').replace(/\\n/g, '\n') : extractField('content');

      const post: GeneratedPost = {
        title: extractField('title'),
        meta_description: extractField('meta_description'),
        h1: extractField('h1'),
        excerpt: extractField('excerpt'),
        quick_facts: extractArray('quick_facts') as GeneratedPost['quick_facts'],
        content: content,
        faq_schema: extractArray('faq_schema') as GeneratedPost['faq_schema'],
        data_tables: extractObj('data_tables') as Record<string, unknown>,
      };

      if (post.title && post.content) {
        console.log('[seo-agent] JSON repair succeeded');
        return post;
      }
    } catch {
      // Repair also failed
    }
    return null;
  }
}

function shortenTextAtWordBoundary(value: string, maxLength: number): string {
  const trimmed = value.trim().replace(/\s+/g, ' ');
  if (trimmed.length <= maxLength) return trimmed;

  const windowed = trimmed.slice(0, maxLength + 1);
  const lastSpace = windowed.lastIndexOf(' ');
  const cutoff = lastSpace >= Math.floor(maxLength * 0.6) ? lastSpace : maxLength;
  return windowed
    .slice(0, cutoff)
    .trim()
    .replace(/[,:;!\-—\s]+$/g, '');
}

function normalizeTitle(title: string, h1?: string): string {
  const baseTitle = (title || h1 || '').trim().replace(/\s+/g, ' ');
  if (!baseTitle) return '';
  if (baseTitle.length <= 70) return baseTitle;

  const strippedDateSuffix = baseTitle.replace(/\s+[—-]\s+[A-Za-z]+(?:day)?(?:,\s*)?\s+\d{4}$/u, '').trim();
  if (strippedDateSuffix.length >= 30 && strippedDateSuffix.length <= 70) {
    return strippedDateSuffix;
  }

  return shortenTextAtWordBoundary(baseTitle, 70);
}

function buildFallbackFaq(post: GeneratedPost): GeneratedPost['faq_schema'] {
  const subject = post.h1?.trim() || post.title?.trim() || 'this matchup';
  const summary = shortenTextAtWordBoundary(
    post.excerpt?.trim()
      || post.meta_description?.trim()
      || 'We break down the market, the matchup, and the main reasons behind the pick.',
    220
  );

  return [
    {
      question: `What is the pick for ${subject}?`,
      answer: summary,
    },
    {
      question: `Why does SportsClaw like ${subject}?`,
      answer: 'The forecast leans on current market prices, team context, recent form, and matchup-specific data.',
    },
  ];
}

function normalizeGeneratedPost(post: GeneratedPost): GeneratedPost {
  const normalizedTitle = normalizeTitle(post.title, post.h1);
  const normalizedMetaDescription = shortenTextAtWordBoundary(post.meta_description || '', 160);
  const normalizedFaq = Array.isArray(post.faq_schema) && post.faq_schema.length > 0
    ? post.faq_schema
    : buildFallbackFaq({ ...post, title: normalizedTitle, meta_description: normalizedMetaDescription });

  if (normalizedTitle && normalizedTitle !== post.title) {
    console.log(`[seo-agent] Normalized title length ${post.title.length} -> ${normalizedTitle.length}`);
  }

  if ((!post.faq_schema || post.faq_schema.length === 0) && normalizedFaq.length > 0) {
    console.log('[seo-agent] Injected fallback FAQ schema');
  }

  return {
    ...post,
    title: normalizedTitle || post.title,
    h1: post.h1 || normalizedTitle || post.title,
    meta_description: normalizedMetaDescription || post.meta_description,
    excerpt: (post.excerpt || '').trim(),
    faq_schema: normalizedFaq,
  };
}

async function callGrok(prompt: string, retries: number = 2): Promise<GeneratedPost | null> {
  const providerPlan: LlmPlan[] = [
    ...SEO_LLM_GATEWAY_MODELS.map((model) => ({
      provider: 'gateway' as const,
      name: `Gateway(${model})`,
      model,
    })),
    ...(CLAUDE_ENABLED ? [{ provider: 'claude' as const, name: `Claude(${CLAUDE_MODEL})` }] : []),
    ...(GROK_API_KEY ? [{ provider: 'grok' as const, name: `Grok(${GROK_MODEL})` }] : []),
  ];

  if (providerPlan.length === 0) {
    console.error('[seo-agent] No LLM providers configured');
    return null;
  }

  for (let providerIndex = 0; providerIndex < providerPlan.length; providerIndex++) {
    const plan = providerPlan[providerIndex];
    const { provider, name } = plan;
    for (let attempt = 0; attempt <= retries; attempt++) {
      try {
        const raw = await callLLMRaw(
          prompt,
          provider,
          plan.provider === 'gateway' ? plan.model : undefined
        );
        if (raw) {
          const parsedPost = parseGeneratedPost(raw);
          const post = parsedPost ? normalizeGeneratedPost(parsedPost) : null;
          if (post) {
            if (provider !== 'grok') {
              console.log(`[seo-agent] ${name} succeeded`);
            }
            return post;
          }
          console.warn(`[seo-agent] ${name} JSON parse failed (attempt ${attempt + 1})`);
        }
      } catch (error) {
        console.error(`[seo-agent] ${name} error (attempt ${attempt + 1}):`, (error as Error).message);
      }
    }

    if (providerIndex < providerPlan.length - 1) {
      console.warn(`[seo-agent] ${name} exhausted, falling back...`);
    }
  }

  console.error('[seo-agent] All LLM providers failed');
  return null;
}

function passesQualityCheck(post: GeneratedPost): boolean {
  if (!post.content || post.content.length < 1500) {
    console.warn('[seo-agent] Quality check failed: content too short', post.content?.length);
    return false;
  }
  if (!post.excerpt || post.excerpt.split(/\s+/).length < 20) {
    console.warn('[seo-agent] Quality check failed: excerpt too short');
    return false;
  }
  if (!post.faq_schema || post.faq_schema.length < 1) {
    console.warn('[seo-agent] Quality check: no FAQ items (non-blocking)');
    // Don't fail on missing FAQs — content is still valuable for SEO
  }
  if (!post.title || post.title.length < 20) {
    console.warn('[seo-agent] Quality check failed: title too short');
    return false;
  }
  return true;
}

// ── PUBLIC API ───────────────────────────────────────────────

export async function generateDailyGamePreviews(date: Date): Promise<number> {
  const leagues = await getActiveLeagues();
  console.log(`[seo-agent] Active leagues for ${dateSlug(date)}:`, leagues);
  let generated = 0;

  for (const league of leagues) {
    const games = await getUpcomingGames(league, date);
    if (games.length === 0) continue;

    console.log(`[seo-agent] ${league}: ${games.length} games to preview`);

    // Get all teams for injury lookup
    const allTeams = [...new Set(games.flatMap(g => [g.homeTeam, g.awayTeam]))];
    const injuries = await getTeamInjuries(league, allTeams);

    for (const game of games) {
      if (generated >= MAX_PREVIEWS_PER_DAY) {
        console.warn(`[seo-agent] Preview sub-cap reached (${MAX_PREVIEWS_PER_DAY}), reserving slots for other content`);
        return generated;
      }
      if (await getTodayPostCount() >= MAX_POSTS_PER_DAY) {
        console.warn('[seo-agent] Daily post limit reached');
        return generated;
      }

      // V2: Enrich with all data sources (stats, props, DVP, form, model picks)
      const enriched = await enrichGameData(game);
      const slugHome = enriched.homeTeamFull || game.homeTeam;
      const slugAway = enriched.awayTeamFull || game.awayTeam;
      const slug = slugify(`${slugAway}-vs-${slugHome}-odds-picks-${dateSlug(date)}`);
      const oldSlug = slugify(`${game.awayTeam}-vs-${game.homeTeam}-odds-picks-${dateSlug(date)}`);
      if (await postExists(slug) || await postExists(oldSlug)) {
        console.log(`[seo-agent] Skipping existing: ${slug}`);
        continue;
      }

      const jobId = await logJob({
        agent_type: 'seo-content',
        job_type: 'game-preview',
        config: { league, gameId: game.id, slug },
        status: 'running',
        started_at: new Date(),
      });

      try {
        const prompt = enrichedGamePreviewPrompt(enriched);
        const post = await callGrok(prompt);

        if (!post) {
          await updateJobStatus(jobId, 'failed', null, 'Grok returned null');
          continue;
        }

        const autoPublish = passesQualityCheck(post);
        const now = new Date();

        const postId = await insertBlogPost({
          slug,
          title: post.title,
          meta_description: post.meta_description,
          h1: post.h1 || post.title,
          content: post.content,
          excerpt: post.excerpt,
          quick_facts: post.quick_facts,
          faq_schema: post.faq_schema,
          data_tables: post.data_tables,
          sport: league.toLowerCase(),
          content_type: 'game-preview',
          league: league.toLowerCase(),
          game_date: date,
          schema_markup: null,
          status: autoPublish ? 'published' : 'draft',
          published_at: autoPublish ? now : null,
        });

        await updateJobStatus(jobId, 'completed', { postId, autoPublish });
        if (autoPublish) submitToIndexNow(slug);
        generated++;
        console.log(`[seo-agent] Created game preview: ${slug} (${autoPublish ? 'published' : 'draft'})`);
      } catch (error) {
        await updateJobStatus(jobId, 'failed', null, (error as Error).message);
        console.error(`[seo-agent] Failed to generate preview for ${slug}:`, error);
      }
    }
  }

  return generated;
}

export async function generatePropsAnalysis(league: string, date: Date): Promise<number> {
  const slug = slugify(`best-${league}-player-props-today-${dateSlug(date)}`);
  if (await postExists(slug)) {
    console.log(`[seo-agent] Props analysis already exists: ${slug}`);
    return 0;
  }

  if (await getTodayPostCount() >= MAX_POSTS_PER_DAY) return 0;

  const games = await getUpcomingGames(league, date);
  if (games.length === 0) return 0;

  const gameIds = games.map((g) => g.id);
  const props = await getPlayerProps(league, gameIds, 40);
  if (props.length < 5) {
    console.log(`[seo-agent] Insufficient props data for ${league}: ${props.length}`);
    return 0;
  }

  const jobId = await logJob({
    agent_type: 'seo-content',
    job_type: 'props-analysis',
    config: { league, slug, propCount: props.length },
    status: 'running',
    started_at: new Date(),
  });

  try {
    const prompt = propsAnalysisPrompt(league, props, date);
    const post = await callGrok(prompt);

    if (!post) {
      await updateJobStatus(jobId, 'failed', null, 'Grok returned null');
      return 0;
    }

    const autoPublish = passesQualityCheck(post);
    const now = new Date();

    await insertBlogPost({
      slug,
      title: post.title,
      meta_description: post.meta_description,
      h1: post.h1 || post.title,
      content: post.content,
      excerpt: post.excerpt,
      quick_facts: post.quick_facts,
      faq_schema: post.faq_schema,
      data_tables: post.data_tables,
      sport: league.toLowerCase(),
      content_type: 'props-analysis',
      league: league.toLowerCase(),
      game_date: date,
      schema_markup: null,
      status: autoPublish ? 'published' : 'draft',
      published_at: autoPublish ? now : null,
    });

    await updateJobStatus(jobId, 'completed', { autoPublish });
    if (autoPublish) submitToIndexNow(slug);
    console.log(`[seo-agent] Created props analysis: ${slug}`);
    return 1;
  } catch (error) {
    await updateJobStatus(jobId, 'failed', null, (error as Error).message);
    return 0;
  }
}

export async function generateLineMovementAlert(league: string): Promise<number> {
  const today = new Date();
  const slug = slugify(`${league}-line-movement-sharp-money-${dateSlug(today)}-${today.getHours()}`);
  if (await postExists(slug)) return 0;
  if (await getTodayPostCount() >= MAX_POSTS_PER_DAY) return 0;

  const movements = await getRecentLineMovements(league, 20);
  const sharpMoves = await getRecentSharpMoves(league, 15);

  if (movements.length < 3 && sharpMoves.length < 2) {
    console.log(`[seo-agent] Insufficient line movement data for ${league}`);
    return 0;
  }

  const jobId = await logJob({
    agent_type: 'seo-content',
    job_type: 'line-movement',
    config: { league, slug, movements: movements.length, sharpMoves: sharpMoves.length },
    status: 'running',
    started_at: new Date(),
  });

  try {
    const prompt = lineMovementPrompt(league, movements, sharpMoves);
    const post = await callGrok(prompt);

    if (!post) {
      await updateJobStatus(jobId, 'failed', null, 'Grok returned null');
      return 0;
    }

    const autoPublish = passesQualityCheck(post);

    await insertBlogPost({
      slug,
      title: post.title,
      meta_description: post.meta_description,
      h1: post.h1 || post.title,
      content: post.content,
      excerpt: post.excerpt,
      quick_facts: post.quick_facts,
      faq_schema: post.faq_schema,
      data_tables: post.data_tables,
      sport: league.toLowerCase(),
      content_type: 'line-movement',
      league: league.toLowerCase(),
      game_date: today,
      schema_markup: null,
      status: autoPublish ? 'published' : 'draft',
      published_at: autoPublish ? new Date() : null,
    });

    await updateJobStatus(jobId, 'completed', { autoPublish });
    if (autoPublish) submitToIndexNow(slug);
    console.log(`[seo-agent] Created line movement alert: ${slug}`);
    return 1;
  } catch (error) {
    await updateJobStatus(jobId, 'failed', null, (error as Error).message);
    return 0;
  }
}

export async function generateWeeklyRecap(league: string): Promise<number> {
  const today = new Date();
  const weekStart = new Date(today);
  weekStart.setDate(today.getDate() - 7);
  const weekLabel = `Week of ${weekStart.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`;

  const slug = slugify(`${league}-betting-recap-${dateSlug(weekStart)}`);
  if (await postExists(slug)) return 0;
  if (await getTodayPostCount() >= MAX_POSTS_PER_DAY) return 0;

  const games = await getCompletedGames(league, weekStart, today);
  if (games.length < 3) {
    console.log(`[seo-agent] Insufficient completed games for ${league} recap: ${games.length}`);
    return 0;
  }

  const clvData = await getCLVResults(league, weekStart, today);

  const jobId = await logJob({
    agent_type: 'seo-content',
    job_type: 'weekly-recap',
    config: { league, slug, games: games.length },
    status: 'running',
    started_at: new Date(),
  });

  try {
    const prompt = weeklyRecapPrompt(league, games, clvData, weekLabel);
    const post = await callGrok(prompt);

    if (!post) {
      await updateJobStatus(jobId, 'failed', null, 'Grok returned null');
      return 0;
    }

    const autoPublish = passesQualityCheck(post);

    await insertBlogPost({
      slug,
      title: post.title,
      meta_description: post.meta_description,
      h1: post.h1 || post.title,
      content: post.content,
      excerpt: post.excerpt,
      quick_facts: post.quick_facts,
      faq_schema: post.faq_schema,
      data_tables: post.data_tables,
      sport: league.toLowerCase(),
      content_type: 'weekly-recap',
      league: league.toLowerCase(),
      game_date: null,
      schema_markup: null,
      status: autoPublish ? 'published' : 'draft',
      published_at: autoPublish ? new Date() : null,
    });

    await updateJobStatus(jobId, 'completed', { autoPublish });
    if (autoPublish) submitToIndexNow(slug);
    console.log(`[seo-agent] Created weekly recap: ${slug}`);
    return 1;
  } catch (error) {
    await updateJobStatus(jobId, 'failed', null, (error as Error).message);
    return 0;
  }
}

export async function generateEducationalGuide(topic?: string): Promise<number> {
  // Pick a random unwritten topic or use provided one
  const selectedTopic = topic || GUIDE_TOPICS[Math.floor(Math.random() * GUIDE_TOPICS.length)];
  const slug = slugify(selectedTopic);

  if (await postExists(slug)) {
    console.log(`[seo-agent] Guide already exists: ${slug}`);
    return 0;
  }
  if (await getTodayPostCount() >= MAX_POSTS_PER_DAY) return 0;

  const jobId = await logJob({
    agent_type: 'seo-content',
    job_type: 'guide',
    config: { topic: selectedTopic, slug },
    status: 'running',
    started_at: new Date(),
  });

  try {
    // Try RAG for enrichment
    let ragContent = '';
    if (await isRAGAvailable()) {
      const ragResult = await queryRAG(selectedTopic, 'sports betting education');
      ragContent = ragResult.answer;
    }

    const prompt = educationalGuidePrompt(selectedTopic, ragContent);
    const post = await callGrok(prompt);

    if (!post) {
      await updateJobStatus(jobId, 'failed', null, 'Grok returned null');
      return 0;
    }

    const autoPublish = passesQualityCheck(post);

    await insertBlogPost({
      slug,
      title: post.title,
      meta_description: post.meta_description,
      h1: post.h1 || post.title,
      content: post.content,
      excerpt: post.excerpt,
      quick_facts: post.quick_facts,
      faq_schema: post.faq_schema,
      data_tables: post.data_tables || {},
      sport: 'general',
      content_type: 'guide',
      league: 'general',
      game_date: null,
      schema_markup: null,
      status: autoPublish ? 'published' : 'draft',
      published_at: autoPublish ? new Date() : null,
    });

    await updateJobStatus(jobId, 'completed', { autoPublish, hadRAG: !!ragContent });
    if (autoPublish) submitToIndexNow(slug);
    console.log(`[seo-agent] Created guide: ${slug}`);
    return 1;
  } catch (error) {
    await updateJobStatus(jobId, 'failed', null, (error as Error).message);
    return 0;
  }
}
