import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import Fastify from 'fastify';
import { scheduleRoutes } from './routes.js';
import { setIpcBridge, type IpcBridge } from '../../lib/ipc-bridge.js';

vi.mock('../../lib/product-config.js', () => ({
  PRODUCT_ID: 'clawcowork',
  productConfig: {
    backendPort: 4009,
  },
}));

vi.mock('../../lib/request-context.js', () => ({
  getUserId: vi.fn(() => 'demo-user'),
}));

let app: ReturnType<typeof Fastify>;
const call = vi.fn();

beforeEach(async () => {
  setIpcBridge({
    isRunning: true,
    call,
  } as unknown as IpcBridge);

  app = Fastify({ logger: false });
  app.decorateRequest('jwtPayload', null);
  await app.register(scheduleRoutes);
  call.mockReset();
});

afterEach(async () => {
  setIpcBridge(null);
  await app.close();
});

describe('schedule routes', () => {
  it('lists schedules via IPC', async () => {
    call.mockResolvedValue({
      result: { schedules: [{ id: 'sched-1', name: 'Daily sync' }] },
    });

    const res = await app.inject({ method: 'GET', url: '/api/schedule' });
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.payload).schedules).toHaveLength(1);
  });

  it('creates schedule via IPC', async () => {
    call.mockResolvedValue({
      result: { schedule: { id: 'sched-1', name: 'Daily sync' } },
    });

    const res = await app.inject({
      method: 'POST',
      url: '/api/schedule',
      payload: {
        name: 'Daily sync',
        goal: 'Refresh the workspace',
        folder: '/tmp/demo',
        schedule: { type: 'interval', seconds: 3600 },
      },
    });

    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.payload).schedule.id).toBe('sched-1');
    expect(call).toHaveBeenCalledWith(
      'create_schedule',
      expect.objectContaining({
        name: 'Daily sync',
      })
    );
  });

  it('updates schedule via IPC', async () => {
    call.mockResolvedValue({
      result: { schedule: { id: 'sched-1', enabled: false } },
    });

    const res = await app.inject({
      method: 'PATCH',
      url: '/api/schedule/sched-1',
      payload: { enabled: false },
    });

    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.payload).schedule.enabled).toBe(false);
  });

  it('deletes schedule via IPC', async () => {
    call.mockResolvedValue({
      result: { deleted: true, scheduleId: 'sched-1' },
    });

    const res = await app.inject({ method: 'DELETE', url: '/api/schedule/sched-1' });
    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.payload).deleted).toBe(true);
  });

  it('pauses schedule via IPC', async () => {
    call.mockResolvedValue({
      result: { schedule: { id: 'sched-1', enabled: false } },
    });

    const res = await app.inject({
      method: 'POST',
      url: '/api/schedule/sched-1/pause',
      payload: { paused: true },
    });

    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.payload).schedule.enabled).toBe(false);
    expect(call).toHaveBeenCalledWith(
      'pause_schedule',
      expect.objectContaining({
        scheduleId: 'sched-1',
        paused: true,
      })
    );
  });

  it('maps missing schedules to 404', async () => {
    call.mockResolvedValue({
      error: { code: -32001, message: 'not found' },
    });

    const res = await app.inject({
      method: 'PATCH',
      url: '/api/schedule/missing',
      payload: { enabled: false },
    });

    expect(res.statusCode).toBe(404);
  });
});
