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

// Mock next/server
vi.mock('next/server', () => ({
  NextResponse: {
    json: (body, init) => ({
      _body: body,
      status: init?.status || 200,
      headers: init?.headers || {},
      json: () => Promise.resolve(body),
    }),
  },
}));

// Mock the validation module — these are pure, let them pass through
vi.mock('@/lib/auth/validation', () => ({
  validateUsername: vi.fn((u) => {
    if (!u || u.trim().length < 3) return { valid: false, error: 'too short' };
    return { valid: true, value: u.trim() };
  }),
  validatePassword: vi.fn((p) => {
    if (!p || p.length < 8) return { valid: false, error: 'too short' };
    return { valid: true };
  }),
}));

vi.mock('@/lib/auth/password', () => ({
  hashPassword: vi.fn((pw) => 'hashed:' + pw),
}));

const mockCreateUser = vi.fn();
const mockFindByUsername = vi.fn();

vi.mock('@/lib/db/repos/usersRepo', () => ({
  createUser: (...args) => mockCreateUser(...args),
  findByUsername: (...args) => mockFindByUsername(...args),
}));

describe('POST /api/auth/register', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('registers a new user successfully', async () => {
    mockFindByUsername.mockResolvedValue(null);
    mockCreateUser.mockResolvedValue({ id: 'user-1', username: 'newuser' });

    const { POST } = await import('./route.js');
    const req = { json: () => Promise.resolve({ username: 'newuser', password: 'password123' }) };
    const res = await POST(req);

    expect(res.status).toBe(201);
    expect(res._body.message).toBe('Registration submitted. Awaiting admin approval.');
    expect(res._body.userId).toBe('user-1');
  });

  it('returns 400 when username is invalid', async () => {
    const { POST } = await import('./route.js');
    const req = { json: () => Promise.resolve({ username: 'ab', password: 'password123' }) };
    const res = await POST(req);

    expect(res.status).toBe(400);
    expect(res._body.error).toBe('too short');
  });

  it('returns 400 when password is invalid', async () => {
    const { POST } = await import('./route.js');
    const req = { json: () => Promise.resolve({ username: 'validuser', password: '123' }) };
    const res = await POST(req);

    expect(res.status).toBe(400);
    expect(res._body.error).toBe('too short');
  });

  it('returns 409 when username already taken', async () => {
    mockFindByUsername.mockResolvedValue({ id: 'existing', username: 'taken' });

    const { POST } = await import('./route.js');
    const req = { json: () => Promise.resolve({ username: 'taken', password: 'password123' }) };
    const res = await POST(req);

    expect(res.status).toBe(409);
    expect(res._body.error).toBe('Username already taken');
  });

  it('returns 400 when body is empty', async () => {
    const { POST } = await import('./route.js');
    const req = { json: () => Promise.resolve(null) };
    const res = await POST(req);

    expect(res.status).toBe(400);
  });

  it('returns 500 on unexpected error', async () => {
    mockFindByUsername.mockRejectedValue(new Error('DB down'));

    const { POST } = await import('./route.js');
    const req = { json: () => Promise.resolve({ username: 'newuser', password: 'password123' }) };
    const res = await POST(req);

    expect(res.status).toBe(500);
    expect(res._body.error).toBe('Registration failed');
  });
});
