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

const createServiceAppMock = vi.fn();
const registerOptionalJwtContextMock = vi.fn(async () => undefined);
const startServiceMock = vi.fn(async () => undefined);

const appMock = {
  register: vi.fn(async () => undefined),
  inject: vi.fn(),
  get: vi.fn(),
  log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
  addHook: vi.fn(),
};

vi.mock('@bytelyst/fastify-core', () => ({
  createServiceApp: createServiceAppMock,
  registerOptionalJwtContext: registerOptionalJwtContextMock,
  startService: startServiceMock,
}));

vi.mock('jose', () => ({ jwtVerify: vi.fn() }));
vi.mock('./modules/health/routes.js', () => ({ healthRoutes: vi.fn() }));
vi.mock('./modules/tasks/routes.js', () => ({ taskRoutes: vi.fn() }));
vi.mock('./lib/config.js', () => ({
  config: {
    PORT: 4009,
    HOST: '0.0.0.0',
    CORS_ORIGIN: undefined,
    SERVICE_NAME: 'cowork-service',
    PLATFORM_SERVICE_URL: 'http://localhost:4003',
    RUST_RUNTIME_BIN: 'cowork-orchestrator',
    RUST_RUNTIME_TIMEOUT_MS: 300_000,
    JWT_SECRET: 'test-secret',
    ANTHROPIC_API_KEY: undefined,
    OLLAMA_URL: 'http://localhost:11434/v1',
    OLLAMA_MODELS: undefined,
    EXTRACTION_SERVICE_URL: 'http://localhost:4005',
  },
}));
vi.mock('./lib/product-config.js', () => ({
  PRODUCT_ID: 'clawcowork',
  productConfig: {
    productId: 'clawcowork',
    displayName: 'Claw Cowork',
    platforms: ['macos', 'linux', 'windows'],
    backendPort: 4009,
  },
}));
vi.mock('./lib/request-context.js', () => ({
  getUserId: vi.fn(() => 'demo-user'),
  getRequestProductId: vi.fn(() => 'clawcowork'),
}));
vi.mock('./lib/ipc-bridge.js', () => ({
  getIpcBridge: vi.fn(() => ({
    isRunning: false,
    start: vi.fn(async () => {
      throw new Error('no binary in test');
    }),
    shutdown: vi.fn(async () => undefined),
    onIncomingRequest: vi.fn(),
  })),
}));
vi.mock('./lib/flush-scheduler.js', () => ({
  getFlushScheduler: vi.fn(() => ({
    isRunning: false,
    start: vi.fn(),
    stop: vi.fn(),
    finalFlush: vi.fn(async () => undefined),
  })),
}));
vi.mock('./lib/llm-router.js', () => ({
  initLlmRouter: vi.fn(() => ({ getProviders: vi.fn(() => []) })),
  getLlmRouter: vi.fn(() => ({ getProviders: vi.fn(() => []) })),
  isLlmRouterReady: vi.fn(() => false),
}));
vi.mock('./modules/llm/routes.js', () => ({ llmRoutes: vi.fn() }));
vi.mock('./modules/audit/routes.js', () => ({ auditRoutes: vi.fn() }));
vi.mock('./modules/usage/routes.js', () => ({ usageRoutes: vi.fn() }));
vi.mock('./modules/notifications/routes.js', () => ({ notificationRoutes: vi.fn() }));
vi.mock('./modules/extraction/routes.js', () => ({ extractionRoutes: vi.fn() }));
vi.mock('./modules/marketplace/routes.js', () => ({ marketplaceRoutes: vi.fn() }));
vi.mock('./modules/sessions/routes.js', () => ({ sessionRoutes: vi.fn() }));
vi.mock('./modules/plugins/routes.js', () => ({ pluginRoutes: vi.fn() }));
vi.mock('./modules/schedule/routes.js', () => ({ scheduleRoutes: vi.fn() }));
vi.mock('./modules/push/routes.js', () => ({ pushRoutes: vi.fn() }));
vi.mock('./modules/agent-runtime/routes.js', () => ({ agentRuntimeRoutes: vi.fn() }));

describe('cowork-service bootstrap', () => {
  beforeEach(() => {
    vi.resetModules();
    vi.clearAllMocks();
    createServiceAppMock.mockResolvedValue(appMock);
    appMock.register.mockReset();
    appMock.register.mockResolvedValue(undefined);
  });

  it('creates app, registers JWT + routes, and starts on port 4009', async () => {
    await import('./server.js');

    expect(createServiceAppMock).toHaveBeenCalledOnce();
    const opts = createServiceAppMock.mock.calls[0][0];
    expect(opts.name).toBe('cowork-service');
    expect(opts.version).toBe('0.1.0');
    expect(opts.readiness).toBe(true);

    // health + task + llm + audit + usage + notifications + extraction + marketplace + sessions + plugins + schedule + push + agent-runtime = 13 register calls + 1 JWT
    expect(registerOptionalJwtContextMock).toHaveBeenCalledOnce();
    expect(appMock.register).toHaveBeenCalledTimes(13);
    expect(startServiceMock).toHaveBeenCalledWith(appMock, { port: 4009, host: '0.0.0.0' });
  });
});
