import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import type { PrismaClient } from '@prisma/client';
import { setupTestDb, cleanupTestDb, clearAllTables } from './helpers.js';

describe('llm virtual-provider schema', () => {
  let prisma: PrismaClient;

  beforeAll(async () => {
    prisma = await setupTestDb();
  }, 30_000);

  afterAll(async () => {
    await cleanupTestDb();
  });

  beforeEach(async () => {
    await clearAllTables(prisma);
  });

  it('defaults a freshly inserted Llm to kind=public, status=active', async () => {
    const llm = await prisma.llm.create({
      data: { name: 'plain', type: 'openai', model: 'gpt-4o' },
    });
    expect(llm.kind).toBe('public');
    expect(llm.status).toBe('active');
    expect(llm.providerSessionId).toBeNull();
    expect(llm.lastHeartbeatAt).toBeNull();
    expect(llm.inactiveSince).toBeNull();
  });

  it('persists kind=virtual + lifecycle fields together', async () => {
    const now = new Date();
    const llm = await prisma.llm.create({
      data: {
        name: 'vllm-local',
        type: 'openai',
        model: 'Qwen/Qwen2.5-7B-Instruct-AWQ',
        kind: 'virtual',
        providerSessionId: 'sess-abc',
        lastHeartbeatAt: now,
        status: 'active',
      },
    });
    expect(llm.kind).toBe('virtual');
    expect(llm.providerSessionId).toBe('sess-abc');
    expect(llm.lastHeartbeatAt?.getTime()).toBe(now.getTime());
    expect(llm.status).toBe('active');
  });

  it('flips status active → inactive and records inactiveSince', async () => {
    const llm = await prisma.llm.create({
      data: {
        name: 'goingaway',
        type: 'openai',
        model: 'm',
        kind: 'virtual',
        providerSessionId: 's1',
      },
    });
    const flippedAt = new Date();
    await prisma.llm.update({
      where: { id: llm.id },
      data: { status: 'inactive', inactiveSince: flippedAt },
    });
    const reloaded = await prisma.llm.findUnique({ where: { id: llm.id } });
    expect(reloaded?.status).toBe('inactive');
    expect(reloaded?.inactiveSince?.getTime()).toBe(flippedAt.getTime());
  });

  it('hibernating is a valid LlmStatus value (reserved for v2 wake path)', async () => {
    const llm = await prisma.llm.create({
      data: {
        name: 'sleepy',
        type: 'openai',
        model: 'm',
        kind: 'virtual',
        providerSessionId: 's-sleep',
        status: 'hibernating',
      },
    });
    expect(llm.status).toBe('hibernating');
  });

  it('rejects unknown enum values for kind / status', async () => {
    await expect(
      prisma.llm.create({
        // Cast through unknown — runtime test of the enum constraint, not TS.
        data: ({ name: 'bad', type: 'openai', model: 'm', kind: 'made-up' } as unknown) as Parameters<typeof prisma.llm.create>[0]['data'],
      }),
    ).rejects.toThrow();

    await expect(
      prisma.llm.create({
        data: ({ name: 'bad2', type: 'openai', model: 'm', status: 'unknown' } as unknown) as Parameters<typeof prisma.llm.create>[0]['data'],
      }),
    ).rejects.toThrow();
  });

  it('finds virtual rows by (kind, status) cheaply', async () => {
    // Mix of public + virtual + assorted statuses — confirms the
    // @@index([kind, status]) covers the GC sweep query.
    await prisma.llm.create({ data: { name: 'pub-1', type: 'openai', model: 'm' } });
    await prisma.llm.create({ data: { name: 'pub-2', type: 'openai', model: 'm' } });
    await prisma.llm.create({
      data: { name: 'v-1', type: 'openai', model: 'm', kind: 'virtual', providerSessionId: 's1', status: 'active' },
    });
    await prisma.llm.create({
      data: { name: 'v-2', type: 'openai', model: 'm', kind: 'virtual', providerSessionId: 's2', status: 'inactive', inactiveSince: new Date() },
    });

    const stale = await prisma.llm.findMany({
      where: { kind: 'virtual', status: 'inactive' },
      select: { name: true },
    });
    expect(stale.map((l) => l.name)).toEqual(['v-2']);
  });

  it('finds rows by providerSessionId (used on mcplocal reconnect)', async () => {
    await prisma.llm.create({
      data: { name: 'a', type: 'openai', model: 'm', kind: 'virtual', providerSessionId: 'shared' },
    });
    await prisma.llm.create({
      data: { name: 'b', type: 'openai', model: 'm', kind: 'virtual', providerSessionId: 'shared' },
    });
    await prisma.llm.create({
      data: { name: 'c', type: 'openai', model: 'm', kind: 'virtual', providerSessionId: 'other' },
    });

    const owned = await prisma.llm.findMany({
      where: { providerSessionId: 'shared' },
      select: { name: true },
      orderBy: { name: 'asc' },
    });
    expect(owned.map((l) => l.name)).toEqual(['a', 'b']);
  });
});
