import { NextRequest, NextResponse } from 'next/server';
import { assertCronAuthorized } from '@/lib/cron/auth';
import { withJobRun } from '@/lib/cron/jobRun';
import { prisma } from '@/lib/db/client';
import { getGamesByIgdbIds } from '@/lib/igdb/client';
import { mapIgdbToFacts, computeSourceDataHash } from '@/lib/content/mapper';
import { loadOverrides } from '@/lib/config/overrides';

export const runtime = 'nodejs';
export const maxDuration = 60;

const STALENESS_DAYS = 30;
const CHECK_BATCH_SIZE = 100;

/**
 * Two jobs in one, both about keeping published pages honest without a
 * human editor:
 *   1. Sync config/overrides.json -> DB: anything newly added to
 *      excludedSlugs gets de-indexed immediately.
 *   2. Re-check IGDB for PUBLISHED games not looked at in STALENESS_DAYS.
 *      If the underlying facts actually changed, flip to NEEDS_REFRESH so
 *      generate-content regenerates it — the page stays live with its old
 *      (still-accurate-enough) content until the new version is ready.
 */
export async function POST(req: NextRequest) {
  const authError = assertCronAuthorized(req);
  if (authError) return authError;

  const outcome = await withJobRun('refresh-stale', async () => {
    const { excludedSlugs } = await loadOverrides();

    const excludedCount = await prisma.game.updateMany({
      where: { slug: { in: excludedSlugs }, status: { not: 'EXCLUDED' } },
      data: { status: 'EXCLUDED' },
    });

    const cutoff = new Date(Date.now() - STALENESS_DAYS * 24 * 60 * 60 * 1000);
    const staleCandidates = await prisma.game.findMany({
      where: {
        status: 'PUBLISHED',
        igdbId: { not: null },
        OR: [{ lastCheckedAt: null }, { lastCheckedAt: { lt: cutoff } }],
      },
      take: CHECK_BATCH_SIZE,
      orderBy: { lastCheckedAt: 'asc' },
    });

    let itemsOk = 0;
    let itemsFailed = 0;
    let flaggedForRefresh = 0;

    if (staleCandidates.length > 0) {
      try {
        const igdbIds = staleCandidates.map((g) => g.igdbId as number);
        const freshData = await getGamesByIgdbIds(igdbIds);
        const freshById = new Map(freshData.map((g) => [g.id, g]));

        for (const game of staleCandidates) {
          const fresh = game.igdbId != null ? freshById.get(game.igdbId) : undefined;
          if (!fresh) {
            // Delisted from IGDB entirely — leave it live but flag it;
            // a human can decide via excludedSlugs if it should come down.
            await prisma.game.update({ where: { id: game.id }, data: { lastCheckedAt: new Date() } });
            itemsOk++;
            continue;
          }

          const freshFacts = mapIgdbToFacts(fresh);
          const newHash = computeSourceDataHash(freshFacts);

          if (newHash !== game.sourceDataHash) {
            await prisma.game.update({
              where: { id: game.id },
              data: {
                status: 'NEEDS_REFRESH',
                summary: freshFacts.summary,
                storyline: freshFacts.storyline,
                genres: freshFacts.genres,
                platforms: freshFacts.platforms,
                developers: freshFacts.developers,
                publishers: freshFacts.publishers,
                gameModes: freshFacts.gameModes,
                ageRating: freshFacts.ageRating,
                igdbRating: freshFacts.rating,
                igdbRatingCount: freshFacts.ratingCount,
                sourceDataHash: newHash,
                lastCheckedAt: new Date(),
              },
            });
            flaggedForRefresh++;
          } else {
            await prisma.game.update({ where: { id: game.id }, data: { lastCheckedAt: new Date() } });
          }
          itemsOk++;
        }
      } catch (err) {
        itemsFailed += staleCandidates.length;
        console.error('[refresh-stale] Batch IGDB re-check failed:', err);
      }
    }

    return {
      itemsSeen: staleCandidates.length,
      itemsOk,
      itemsFailed,
      detail: { newlyExcluded: excludedCount.count, flaggedForRefresh },
    };
  });

  return NextResponse.json(outcome);
}

// Vercel Cron invokes scheduled routes with GET; scripts/run-cron.ts (for
// VPS/manual use) uses POST. Both are accepted and do the same thing.
export const GET = POST;
