/**
 * Tests for /api/tokens routes
 *
 * Routes proxy to platform-service via JWT — no direct Cosmos or role checks.
 *
 * Covers:
 * - GET  /api/tokens       — 401 without JWT, 200 with list
 * - POST /api/tokens       — 400 missing name, 201 on success
 * - PATCH /api/tokens/[id] — 401 without JWT, 200 on success
 * - DELETE /api/tokens/[id] — 401 without JWT, 200 on success
 */

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

// ── Mocks ──────────────────────────────────────────────────────────────

const mockListTokens = vi.fn();
const mockCreateToken = vi.fn();
const mockRevokeToken = vi.fn();
const mockDeleteToken = vi.fn();
vi.mock('@/lib/platform-client', () => ({
  listTokens: (...args: unknown[]) => mockListTokens(...args),
  createToken: (...args: unknown[]) => mockCreateToken(...args),
  revokeToken: (...args: unknown[]) => mockRevokeToken(...args),
  deleteToken: (...args: unknown[]) => mockDeleteToken(...args),
}));

import { GET, POST } from '@/app/api/tokens/route';
import { PATCH, DELETE } from '@/app/api/tokens/[id]/route';

// ── Helpers ────────────────────────────────────────────────────────────

async function callGet(token?: string) {
  const { NextRequest } = await import('next/server');
  const headers: Record<string, string> = {};
  if (token) headers['Authorization'] = `Bearer ${token}`;
  return GET(new NextRequest(new Request('http://localhost:3001/api/tokens', { headers })));
}

async function callPost(body: object, token = 'admin_jwt') {
  const { NextRequest } = await import('next/server');
  return POST(
    new NextRequest(
      new Request('http://localhost:3001/api/tokens', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
        body: JSON.stringify(body),
      })
    )
  );
}

async function callPatch(id: string, token = 'admin_jwt') {
  const { NextRequest } = await import('next/server');
  return PATCH(
    new NextRequest(
      new Request(`http://localhost:3001/api/tokens/${id}`, {
        method: 'PATCH',
        headers: { Authorization: `Bearer ${token}` },
      })
    ),
    { params: Promise.resolve({ id }) }
  );
}

async function callDelete(id: string, token = 'admin_jwt') {
  const { NextRequest } = await import('next/server');
  return DELETE(
    new NextRequest(
      new Request(`http://localhost:3001/api/tokens/${id}`, {
        method: 'DELETE',
        headers: { Authorization: `Bearer ${token}` },
      })
    ),
    { params: Promise.resolve({ id }) }
  );
}

// ── Tests ──────────────────────────────────────────────────────────────

describe('GET /api/tokens', () => {
  beforeEach(() => vi.clearAllMocks());

  it('returns 401 when no JWT', async () => {
    const res = await callGet();
    expect(res.status).toBe(401);
  });

  it('returns token list on success', async () => {
    mockListTokens.mockResolvedValue([
      { id: 'tok_1', name: 'CI Token', prefix: 'lysnr_abc', status: 'active' },
    ]);
    const res = await callGet('valid_jwt');
    expect(res.status).toBe(200);
    const data = await res.json();
    expect(data).toHaveLength(1);
    expect(data[0].name).toBe('CI Token');
  });
});

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

  it('returns 400 when name is missing', async () => {
    const res = await callPost({});
    expect(res.status).toBe(400);
  });

  it('creates token and returns with 201', async () => {
    mockCreateToken.mockResolvedValue({
      id: 'tok_new',
      name: 'CI',
      prefix: 'lysnr_xyz',
      rawToken: 'lysnr_xyz_full_secret',
      status: 'active',
    });
    const res = await callPost({ name: 'CI', scopes: ['dictation'] });
    expect(res.status).toBe(201);
    const data = await res.json();
    expect(data.rawToken).toBe('lysnr_xyz_full_secret');
  });
});

describe('PATCH /api/tokens/[id]', () => {
  beforeEach(() => vi.clearAllMocks());

  it('returns 401 without JWT', async () => {
    const { NextRequest } = await import('next/server');
    const res = await PATCH(
      new NextRequest(new Request('http://localhost:3001/api/tokens/tok_1', { method: 'PATCH' })),
      { params: Promise.resolve({ id: 'tok_1' }) }
    );
    expect(res.status).toBe(401);
  });

  it('revokes token successfully', async () => {
    mockRevokeToken.mockResolvedValue({ success: true, id: 'tok_1' });
    const res = await callPatch('tok_1');
    expect(res.status).toBe(200);
  });
});

describe('DELETE /api/tokens/[id]', () => {
  beforeEach(() => vi.clearAllMocks());

  it('returns 401 without JWT', async () => {
    const { NextRequest } = await import('next/server');
    const res = await DELETE(
      new NextRequest(new Request('http://localhost:3001/api/tokens/tok_1', { method: 'DELETE' })),
      { params: Promise.resolve({ id: 'tok_1' }) }
    );
    expect(res.status).toBe(401);
  });

  it('deletes token successfully', async () => {
    mockDeleteToken.mockResolvedValue({ success: true, id: 'tok_1' });
    const res = await callDelete('tok_1');
    expect(res.status).toBe(200);
  });
});
