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

const mocked = vi.hoisted(() => ({
  poolQuery: vi.fn(),
}));

vi.mock('../../db', () => ({
  default: {
    query: mocked.poolQuery,
  },
}));

describe('auth middleware', () => {
  const validUserId = '550e8400-e29b-41d4-a716-446655440000';

  beforeEach(() => {
    vi.clearAllMocks();
    process.env.JWT_SECRET = 'test-secret';
  });

  it('accepts tokens whose auth version still matches the user record', async () => {
    vi.resetModules();
    mocked.poolQuery.mockResolvedValue({ rows: [{ auth_token_version: 0 }] });

    const { authMiddleware, signToken } = await import('../auth');
    const token = signToken({ userId: validUserId, email: 'user@example.com', tokenVersion: 0 });

    const app = express();
    app.get('/secure', authMiddleware, (req, res) => res.json({ user: req.user }));

    const res = await request(app)
      .get('/secure')
      .set('Authorization', `Bearer ${token}`);

    expect(res.status).toBe(200);
    expect(res.body.user).toMatchObject({
      userId: validUserId,
      email: 'user@example.com',
      tokenVersion: 0,
    });
    expect(mocked.poolQuery).toHaveBeenCalledWith(
      'SELECT auth_token_version FROM rm_users WHERE id = $1',
      [validUserId],
    );
  });

  it('accepts auth cookies when no bearer header is present', async () => {
    vi.resetModules();
    mocked.poolQuery.mockResolvedValue({ rows: [{ auth_token_version: 0 }] });

    const { authMiddleware, signToken } = await import('../auth');
    const token = signToken({ userId: validUserId, email: 'user@example.com', tokenVersion: 0 });

    const app = express();
    app.use((req, _res, next) => {
      req.cookies = { rm_auth: token };
      next();
    });
    app.get('/secure', authMiddleware, (req, res) => res.json({ user: req.user }));

    const res = await request(app).get('/secure');

    expect(res.status).toBe(200);
    expect(res.body.user).toMatchObject({
      userId: validUserId,
      email: 'user@example.com',
      tokenVersion: 0,
    });
  });

  it('rejects tokens after password-driven auth version rotation', async () => {
    vi.resetModules();
    mocked.poolQuery.mockResolvedValue({ rows: [{ auth_token_version: 1 }] });

    const { authMiddleware, signToken } = await import('../auth');
    const token = signToken({ userId: validUserId, email: 'user@example.com', tokenVersion: 0 });

    const app = express();
    app.get('/secure', authMiddleware, (_req, res) => res.json({ ok: true }));

    const res = await request(app)
      .get('/secure')
      .set('Authorization', `Bearer ${token}`);

    expect(res.status).toBe(401);
    expect(res.body).toEqual({ error: 'Invalid or expired token' });
  });

  it('rejects non-uuid user ids before they hit the database', async () => {
    vi.resetModules();

    const { authMiddleware, signToken } = await import('../auth');
    const token = signToken({ userId: '1' as any, email: 'user@example.com', tokenVersion: 0 });

    const app = express();
    app.get('/secure', authMiddleware, (_req, res) => res.json({ ok: true }));

    const res = await request(app)
      .get('/secure')
      .set('Authorization', `Bearer ${token}`);

    expect(res.status).toBe(401);
    expect(res.body).toEqual({ error: 'Invalid or expired token' });
    expect(mocked.poolQuery).not.toHaveBeenCalled();
  });
});
