import { beforeEach, describe, expect, it } from 'vitest';
import { handleActuateAPI } from '../../api/index.js';
import { createSession } from '../../auth/session.js';
import { initDB } from '../../db.js';
import { recordAiCrawlerHit } from '../../seo/ai-crawler-log.js';
/**
 * AI-crawler activity layer:
 *   - GET /ai-crawlers/trap (honeypot) records a hit and serves 200 + noindex
 *   - GET /llms.txt records a hit when fetched by a known AI user-agent
 *   - GET /llms.txt appends the honeypot link only when opted in
 *   - GET /ai-crawlers is admin-only and returns aggregated totals
 *   - recordAiCrawlerHit no-ops on databases without the model
 */
const SECRET = 'ai-crawler-suite-secret-0123456789abcdef';
const GPTBOT_UA = 'Mozilla/5.0 (compatible; GPTBot/1.1; +https://openai.com/gptbot)';
function createMockDB(opts = {}) {
    const upserts = [];
    const db = {
        document: {
            findMany: async () => [],
            findFirst: async () => null,
            count: async () => 0,
        },
        user: {},
        session: {},
        media: {},
        auditLog: { create: async () => ({}) },
    };
    if (opts.hasCrawlerModel !== false) {
        db.aiCrawlerHit = {
            upsert: async (args) => {
                upserts.push(args);
                return { id: 'x' };
            },
            findMany: async () => opts.rows ?? [],
        };
    }
    db.__upserts = upserts;
    return db;
}
function makeHandler(db, llms = {}) {
    initDB(db);
    return handleActuateAPI({
        prismaClient: db,
        config: {
            seo: { siteName: 'Acme', siteUrl: 'https://example.com', llms },
            collections: {
                pages: {
                    slug: 'pages',
                    type: 'page',
                    labels: { singular: 'Page', plural: 'Pages' },
                    fields: {},
                },
            },
        },
    });
}
async function adminCookie() {
    const token = await createSession({ userId: 'admin-1', role: 'ADMIN', sessionId: 'sess-1' }, { secret: SECRET });
    return `actuate_session=${token}`;
}
describe('GET /ai-crawlers/trap (honeypot)', () => {
    beforeEach(() => {
        delete globalThis.__actuateConfig;
        process.env.CMS_SECRET = SECRET;
    });
    it('records a honeypot hit attributed to the detected bot, served noindex', async () => {
        const db = createMockDB();
        const handler = makeHandler(db);
        const res = await handler(new Request('https://example.com/api/cms/ai-crawlers/trap', {
            headers: { 'user-agent': GPTBOT_UA },
        }));
        expect(res.status).toBe(200);
        expect(res.headers.get('x-robots-tag')).toBe('noindex');
        expect(db.__upserts).toHaveLength(1);
        expect(db.__upserts[0].where).toEqual({ bot_surface: { bot: 'GPTBot', surface: 'honeypot' } });
        expect(db.__upserts[0].create).toMatchObject({
            bot: 'GPTBot',
            surface: 'honeypot',
            category: 'training',
        });
    });
    it('attributes an unknown user-agent to "unknown"', async () => {
        const db = createMockDB();
        const handler = makeHandler(db);
        const res = await handler(new Request('https://example.com/api/cms/ai-crawlers/trap'));
        expect(res.status).toBe(200);
        expect(db.__upserts[0].create).toMatchObject({ bot: 'unknown', surface: 'honeypot' });
    });
});
describe('GET /llms.txt crawler recording + honeypot link', () => {
    beforeEach(() => {
        delete globalThis.__actuateConfig;
        process.env.CMS_SECRET = SECRET;
    });
    it('records a hit on the "llms" surface for a known AI user-agent', async () => {
        const db = createMockDB();
        const handler = makeHandler(db);
        const res = await handler(new Request('https://example.com/api/cms/llms.txt', {
            headers: { 'user-agent': GPTBOT_UA },
        }));
        expect(res.status).toBe(200);
        expect(db.__upserts.some((u) => u.where.bot_surface?.surface === 'llms')).toBe(true);
    });
    it('does not record for a human user-agent', async () => {
        const db = createMockDB();
        const handler = makeHandler(db);
        await handler(new Request('https://example.com/api/cms/llms.txt', {
            headers: { 'user-agent': 'Mozilla/5.0 Chrome/120 Safari' },
        }));
        expect(db.__upserts).toHaveLength(0);
    });
    it('appends the honeypot link only when seo.llms.honeypot is enabled', async () => {
        const withTrap = makeHandler(createMockDB(), { honeypot: true });
        const onRes = await withTrap(new Request('https://example.com/api/cms/llms.txt'));
        const onText = await onRes.text();
        expect(onText).toContain('/api/cms/ai-crawlers/trap');
        delete globalThis.__actuateConfig;
        const withoutTrap = makeHandler(createMockDB());
        const offRes = await withoutTrap(new Request('https://example.com/api/cms/llms.txt'));
        const offText = await offRes.text();
        expect(offText).not.toContain('/api/cms/ai-crawlers/trap');
    });
});
describe('GET /ai-crawlers (admin panel data)', () => {
    beforeEach(() => {
        delete globalThis.__actuateConfig;
        process.env.CMS_SECRET = SECRET;
    });
    it('401s for an unauthenticated request', async () => {
        const handler = makeHandler(createMockDB());
        const res = await handler(new Request('https://example.com/api/cms/ai-crawlers'));
        expect(res.status).toBe(401);
    });
    it('returns aggregated totals for an authenticated admin', async () => {
        const rows = [
            {
                bot: 'GPTBot',
                surface: 'llms',
                category: 'training',
                hits: 5,
                firstSeenAt: new Date(),
                lastSeenAt: new Date(),
            },
            {
                bot: 'PerplexityBot',
                surface: 'md',
                category: 'retrieval',
                hits: 3,
                firstSeenAt: new Date(),
                lastSeenAt: new Date(),
            },
            {
                bot: 'unknown',
                surface: 'honeypot',
                category: 'other',
                hits: 2,
                firstSeenAt: new Date(),
                lastSeenAt: new Date(),
            },
        ];
        const handler = makeHandler(createMockDB({ rows }));
        const res = await handler(new Request('https://example.com/api/cms/ai-crawlers', {
            headers: { cookie: await adminCookie() },
        }));
        expect(res.status).toBe(200);
        const body = (await res.json());
        expect(body.data.enabled).toBe(true);
        expect(body.data.rows).toHaveLength(3);
        expect(body.data.totals).toEqual({ hits: 10, bots: 3, honeypot: 2 });
    });
    it('reports enabled:false when the database predates the model', async () => {
        const handler = makeHandler(createMockDB({ hasCrawlerModel: false }));
        const res = await handler(new Request('https://example.com/api/cms/ai-crawlers', {
            headers: { cookie: await adminCookie() },
        }));
        expect(res.status).toBe(200);
        const body = (await res.json());
        expect(body.data.enabled).toBe(false);
    });
});
describe('recordAiCrawlerHit', () => {
    it('no-ops when the model is absent', async () => {
        const db = createMockDB({ hasCrawlerModel: false });
        await expect(recordAiCrawlerHit(db, { bot: 'GPTBot', surface: 'llms', category: 'training' })).resolves.toBeUndefined();
    });
    it('upserts on the (bot, surface) compound key', async () => {
        const db = createMockDB();
        await recordAiCrawlerHit(db, { bot: 'CCBot', surface: 'md', category: 'training' });
        expect(db.__upserts[0].where).toEqual({ bot_surface: { bot: 'CCBot', surface: 'md' } });
        expect(db.__upserts[0].update.hits).toEqual({ increment: 1 });
    });
});
//# sourceMappingURL=ai-crawlers.test.js.map