import { describe, it, expect, beforeEach, vi } from 'vitest';

const { getCurrentUserMock } = vi.hoisted(() => ({
  getCurrentUserMock: vi.fn(),
}));
vi.mock('@/lib/auth', async (orig) => {
  const actual = (await orig()) as Record<string, unknown>;
  return { ...actual, getCurrentUser: getCurrentUserMock };
});

import { NextRequest } from 'next/server';
import { prisma } from '@/lib/prisma';
import { POST } from '@/app/api/ai/generate-workout/route';

const URL = 'http://x/api/ai/generate-workout';

function jsonReq(body: unknown): NextRequest {
  return new NextRequest(URL, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(body),
  } as ConstructorParameters<typeof NextRequest>[1]);
}
function rawReq(rawBody: string): NextRequest {
  return new NextRequest(URL, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: rawBody,
  } as ConstructorParameters<typeof NextRequest>[1]);
}

async function makeUser(email: string) {
  return prisma.user.create({
    data: { email, passwordHash: 'fake', isAdmin: false },
  });
}

beforeEach(async () => {
  await prisma.aIGeneration.deleteMany();
  await prisma.userPreferences.deleteMany();
  await prisma.user.deleteMany();
  getCurrentUserMock.mockReset();
});

describe('POST /api/ai/generate-workout — auth + validation', () => {
  // These all return BEFORE the background runner is kicked off, so no
  // real provider call happens. We deliberately don't exercise the 201
  // path (it would spawn a detached generation).

  it('401 when unauthenticated', async () => {
    getCurrentUserMock.mockResolvedValue(null);
    const res = await POST(jsonReq({ userInput: 'upper body' }));
    expect(res.status).toBe(401);
  });

  it('400 on malformed JSON (not 500)', async () => {
    const user = await makeUser('a@x');
    getCurrentUserMock.mockResolvedValue(user);
    const res = await POST(rawReq('{ not valid json'));
    expect(res.status).toBe(400);
  });

  it('400 when userInput is missing', async () => {
    const user = await makeUser('b@x');
    getCurrentUserMock.mockResolvedValue(user);
    const res = await POST(jsonReq({ includeHistory: true }));
    expect(res.status).toBe(400);
  });

  it('400 when userInput is empty', async () => {
    const user = await makeUser('c@x');
    getCurrentUserMock.mockResolvedValue(user);
    const res = await POST(jsonReq({ userInput: '' }));
    expect(res.status).toBe(400);
  });

  it('400 with a malformed priorWorkout (fails the shared schema)', async () => {
    const user = await makeUser('d@x');
    getCurrentUserMock.mockResolvedValue(user);
    // priorWorkout missing required `exercises` array → schema rejects.
    const res = await POST(
      jsonReq({ userInput: 'tweak it', priorWorkout: { name: 'X' } }),
    );
    expect(res.status).toBe(400);
  });

  it('400 when the user has no AI provider configured', async () => {
    const user = await makeUser('e@x');
    getCurrentUserMock.mockResolvedValue(user);
    // Valid body, but no UserPreferences row → not configured.
    const res = await POST(jsonReq({ userInput: 'upper body, shoulders' }));
    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.error).toMatch(/not configured/i);
  });
});
