/**
 * Component: Admin API Tokens Route Tests
 * Documentation: documentation/testing.md
 */

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { createPrismaMock } from '../helpers/prisma';

// Valid UUIDs for testing
const ADMIN_ID = '11111111-1111-1111-1111-111111111111';
const USER_ID = '22222222-2222-2222-2222-222222222222';
const ADMIN2_ID = '33333333-3333-3333-3333-333333333333';
const NONEXISTENT_ID = '99999999-9999-9999-9999-999999999999';

let authRequest: any;

const prismaMock = createPrismaMock();
const requireAuthMock = vi.hoisted(() => vi.fn());
const requireAdminMock = vi.hoisted(() => vi.fn());
const checkApiTokenCreateRateLimitMock = vi.hoisted(() => vi.fn());
const generateApiTokenMock = vi.hoisted(() => vi.fn());

vi.mock('@/lib/db', () => ({
  prisma: prismaMock,
}));

vi.mock('@/lib/middleware/auth', () => ({
  requireAuth: requireAuthMock,
  requireAdmin: requireAdminMock,
}));

vi.mock('@/lib/utils/rateLimit', () => ({
  checkApiTokenCreateRateLimit: checkApiTokenCreateRateLimitMock,
}));

vi.mock('@/lib/utils/api-token', () => ({
  generateApiToken: generateApiTokenMock,
}));

describe('Admin API tokens routes', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    authRequest = {
      user: { id: ADMIN_ID, username: 'admin', role: 'admin' },
      json: vi.fn(),
    };
    requireAuthMock.mockImplementation((_req: any, handler: any) => handler(authRequest));
    requireAdminMock.mockImplementation((_req: any, handler: any) => handler());
    checkApiTokenCreateRateLimitMock.mockReturnValue({ allowed: true });
    generateApiTokenMock.mockReturnValue({
      fullToken: 'rmab_test_full_token',
      tokenHash: 'hashed_token',
      tokenPrefix: 'rmab_test',
    });
  });

  describe('POST /api/admin/api-tokens', () => {
    it('creates token for self with own role when no userId specified', async () => {
      authRequest.json.mockResolvedValueOnce({ name: 'Test Token' });

      prismaMock.user.findUnique.mockResolvedValueOnce({
        id: ADMIN_ID,
        role: 'admin',
        plexUsername: 'admin',
      });
      prismaMock.apiToken.count.mockResolvedValueOnce(0);
      prismaMock.apiToken.create.mockResolvedValueOnce({
        id: 'token-1',
        name: 'Test Token',
        tokenPrefix: 'rmab_test',
        role: 'admin',
        expiresAt: null,
        createdAt: new Date(),
      });

      const { POST } = await import('@/app/api/admin/api-tokens/route');
      const response = await POST({} as any);
      const payload = await response.json();

      expect(response.status).toBe(201);
      expect(payload.token.role).toBe('admin');
      expect(payload.fullToken).toBe('rmab_test_full_token');
    });

    it('creates token for another user with their role', async () => {
      authRequest.json.mockResolvedValueOnce({
        name: 'Token for User',
        userId: USER_ID,
      });

      prismaMock.user.findUnique.mockResolvedValueOnce({
        id: USER_ID,
        role: 'user',
        plexUsername: 'regularuser',
      });
      prismaMock.apiToken.count.mockResolvedValueOnce(0);
      prismaMock.apiToken.create.mockResolvedValueOnce({
        id: 'token-2',
        name: 'Token for User',
        tokenPrefix: 'rmab_test',
        role: 'user',
        expiresAt: null,
        createdAt: new Date(),
      });

      const { POST } = await import('@/app/api/admin/api-tokens/route');
      const response = await POST({} as any);
      const payload = await response.json();

      expect(response.status).toBe(201);
      expect(payload.token.role).toBe('user');
    });

    it('rejects role override when role differs from target user role', async () => {
      authRequest.json.mockResolvedValueOnce({
        name: 'Escalation Attempt',
        userId: USER_ID,
        role: 'admin', // Trying to give admin role to a regular user
      });

      prismaMock.user.findUnique.mockResolvedValueOnce({
        id: USER_ID,
        role: 'user', // Target user is actually a regular user
        plexUsername: 'regularuser',
      });

      const { POST } = await import('@/app/api/admin/api-tokens/route');
      const response = await POST({} as any);
      const payload = await response.json();

      expect(response.status).toBe(400);
      expect(payload.error).toContain("must match target user's role");
    });

    it('rejects role downgrade when role differs from target user role', async () => {
      authRequest.json.mockResolvedValueOnce({
        name: 'Downgrade Attempt',
        userId: ADMIN2_ID,
        role: 'user', // Trying to give user role to an admin
      });

      prismaMock.user.findUnique.mockResolvedValueOnce({
        id: ADMIN2_ID,
        role: 'admin', // Target user is actually an admin
        plexUsername: 'otheradmin',
      });

      const { POST } = await import('@/app/api/admin/api-tokens/route');
      const response = await POST({} as any);
      const payload = await response.json();

      expect(response.status).toBe(400);
      expect(payload.error).toContain("must match target user's role");
    });

    it('accepts role when it matches target user role', async () => {
      authRequest.json.mockResolvedValueOnce({
        name: 'Matching Role',
        userId: USER_ID,
        role: 'user', // Explicitly specifying role that matches
      });

      prismaMock.user.findUnique.mockResolvedValueOnce({
        id: USER_ID,
        role: 'user',
        plexUsername: 'regularuser',
      });
      prismaMock.apiToken.count.mockResolvedValueOnce(0);
      prismaMock.apiToken.create.mockResolvedValueOnce({
        id: 'token-3',
        name: 'Matching Role',
        tokenPrefix: 'rmab_test',
        role: 'user',
        expiresAt: null,
        createdAt: new Date(),
      });

      const { POST } = await import('@/app/api/admin/api-tokens/route');
      const response = await POST({} as any);

      expect(response.status).toBe(201);
    });

    it('returns 404 when target user does not exist', async () => {
      authRequest.json.mockResolvedValueOnce({
        name: 'Token for Ghost',
        userId: NONEXISTENT_ID,
      });

      prismaMock.user.findUnique.mockResolvedValueOnce(null);

      const { POST } = await import('@/app/api/admin/api-tokens/route');
      const response = await POST({} as any);
      const payload = await response.json();

      expect(response.status).toBe(404);
      expect(payload.error).toBe('Target user not found');
    });

    it('returns 429 when rate limited', async () => {
      checkApiTokenCreateRateLimitMock.mockReturnValueOnce({
        allowed: false,
        retryAfterSeconds: 60,
      });

      authRequest.json.mockResolvedValueOnce({ name: 'Rate Limited Token' });

      const { POST } = await import('@/app/api/admin/api-tokens/route');
      const response = await POST({} as any);

      expect(response.status).toBe(429);
      expect(response.headers.get('Retry-After')).toBe('60');
    });
  });
});
