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

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

const prismaMock = createPrismaMock();
const bcryptMock = vi.hoisted(() => ({
  hash: vi.fn(),
}));
const encryptionMock = vi.hoisted(() => ({
  encrypt: vi.fn((value: string) => `enc-${value}`),
}));
const generateAccessTokenMock = vi.hoisted(() => vi.fn(() => 'access-token'));
const generateRefreshTokenMock = vi.hoisted(() => vi.fn(() => 'refresh-token'));

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

vi.mock('bcrypt', () => ({
  default: bcryptMock,
  ...bcryptMock,
}));

vi.mock('@/lib/services/encryption.service', () => ({
  getEncryptionService: () => encryptionMock,
}));

vi.mock('@/lib/utils/jwt', () => ({
  generateAccessToken: generateAccessTokenMock,
  generateRefreshToken: generateRefreshTokenMock,
}));

describe('Setup routes', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('returns setup status from configuration', async () => {
    prismaMock.configuration.findUnique.mockResolvedValueOnce({ value: 'true' });
    const { GET } = await import('@/app/api/setup/status/route');

    const response = await GET({} as any);
    const payload = await response.json();

    expect(payload.setupComplete).toBe(true);
  });

  it('rejects invalid backend mode on setup completion', async () => {
    const { POST } = await import('@/app/api/setup/complete/route');

    const response = await POST({ json: vi.fn().mockResolvedValue({ backendMode: 'invalid' }) } as any);
    const payload = await response.json();

    expect(response.status).toBe(400);
    expect(payload.error).toMatch(/Invalid or missing backend mode/);
  });

  it('completes setup for Plex mode and returns tokens', async () => {
    bcryptMock.hash.mockResolvedValue('hashed');
    prismaMock.user.create.mockResolvedValue({
      id: 'admin-1',
      plexId: 'local-admin',
      plexUsername: 'admin',
      plexEmail: null,
      role: 'admin',
      avatarUrl: null,
    });
    prismaMock.configuration.upsert.mockResolvedValue({});
    prismaMock.scheduledJob.updateMany.mockResolvedValue({ count: 1 });
    prismaMock.bookDateConfig.findFirst.mockResolvedValue(null);

    const { POST } = await import('@/app/api/setup/complete/route');
    const response = await POST({
      json: vi.fn().mockResolvedValue({
        backendMode: 'plex',
        admin: { username: 'admin', password: 'pass' },
        plex: { url: 'http://plex', token: 'token', audiobook_library_id: 'lib', machine_identifier: 'machine' },
        prowlarr: { url: 'http://prowlarr', api_key: 'key', indexers: [{ id: 1 }] },
        downloadClient: { type: 'qbittorrent', url: 'http://qbt', username: 'u', password: 'p' },
        paths: { download_dir: '/downloads', media_dir: '/media' },
      }),
    } as any);
    const payload = await response.json();

    expect(payload.success).toBe(true);
    expect(payload.accessToken).toBe('access-token');
    expect(prismaMock.configuration.upsert).toHaveBeenCalled();
  });

  it('completes setup for Audiobookshelf with both auth methods', async () => {
    bcryptMock.hash.mockResolvedValue('hashed');
    prismaMock.user.create.mockResolvedValue({
      id: 'admin-2',
      plexId: 'local-admin',
      plexUsername: 'admin',
      plexEmail: null,
      role: 'admin',
      avatarUrl: null,
    });
    prismaMock.configuration.upsert.mockResolvedValue({});
    prismaMock.scheduledJob.updateMany.mockResolvedValue({ count: 1 });
    prismaMock.bookDateConfig.findFirst.mockResolvedValue({ id: 'bookdate-1' });
    prismaMock.bookDateConfig.update.mockResolvedValue({});

    const { POST } = await import('@/app/api/setup/complete/route');
    const response = await POST({
      json: vi.fn().mockResolvedValue({
        backendMode: 'audiobookshelf',
        admin: { username: 'admin', password: 'pass' },
        authMethod: 'both',
        audiobookshelf: {
          server_url: 'http://abs',
          api_token: 'abs-token',
          library_id: 'lib',
          trigger_scan_after_import: true,
        },
        oidc: {
          provider_name: 'OIDC',
          issuer_url: 'https://issuer',
          client_id: 'client-id',
          client_secret: 'client-secret',
          access_control_method: 'open',
          access_group_claim: 'groups',
          access_group_value: '',
          allowed_emails: '[]',
          allowed_usernames: '[]',
          admin_claim_enabled: 'true',
          admin_claim_name: 'groups',
          admin_claim_value: 'admins',
        },
        registration: { require_admin_approval: true },
        prowlarr: { url: 'http://prowlarr', api_key: 'key', indexers: [{ id: 1 }] },
        downloadClient: {
          type: 'qbittorrent',
          url: 'http://qbt',
          username: 'u',
          password: 'p',
          disableSSLVerify: true,
          remotePathMappingEnabled: true,
          remotePath: '/remote',
          localPath: '/local',
        },
        paths: {
          download_dir: '/downloads',
          media_dir: '/media',
          metadata_tagging_enabled: false,
          chapter_merging_enabled: true,
        },
        bookdate: { provider: 'openai', apiKey: 'bd-key', model: 'gpt-4' },
      }),
    } as any);
    const payload = await response.json();

    expect(payload.success).toBe(true);
    expect(payload.accessToken).toBe('access-token');
    expect(prismaMock.bookDateConfig.update).toHaveBeenCalled();
  });

  it('completes setup for Audiobookshelf without admin user', async () => {
    prismaMock.configuration.upsert.mockResolvedValue({});
    prismaMock.scheduledJob.updateMany.mockResolvedValue({ count: 1 });
    prismaMock.bookDateConfig.findFirst.mockResolvedValue(null);

    const { POST } = await import('@/app/api/setup/complete/route');
    const response = await POST({
      json: vi.fn().mockResolvedValue({
        backendMode: 'audiobookshelf',
        authMethod: 'oidc',
        audiobookshelf: {
          server_url: 'http://abs',
          api_token: 'abs-token',
          library_id: 'lib',
        },
        oidc: {
          provider_name: 'OIDC',
          issuer_url: 'https://issuer',
          client_id: 'client-id',
          client_secret: 'client-secret',
        },
        prowlarr: { url: 'http://prowlarr', api_key: 'key', indexers: [{ id: 1 }] },
        downloadClient: { type: 'qbittorrent', url: 'http://qbt', username: 'u', password: 'p' },
        paths: { download_dir: '/downloads', media_dir: '/media' },
      }),
    } as any);
    const payload = await response.json();

    expect(payload.success).toBe(true);
    expect(payload.accessToken).toBeUndefined();
  });
});


