import { describe, expect, it, vi, beforeAll, afterAll, beforeEach } from 'vitest';
import { createServiceApp, type FastifyApp } from '@bytelyst/fastify-core';

vi.mock('../../lib/config.js', () => ({
  config: {
    SERVICE_NAME: 'cowork-service',
    PLATFORM_SERVICE_URL: 'http://mock-platform:4003',
  },
}));
vi.mock('../../lib/product-config.js', () => ({
  PRODUCT_ID: 'clawcowork',
}));
vi.mock('../../lib/request-context.js', () => ({
  getUserId: vi.fn(() => 'demo-user'),
}));

import { notificationRoutes } from './routes.js';

let app: FastifyApp;

const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch);

beforeAll(async () => {
  app = await createServiceApp({
    name: 'notification-routes-test',
    version: '0.0.1',
    logger: false,
  });
  await app.register(notificationRoutes);
});

afterAll(async () => {
  await app.close();
});

beforeEach(() => {
  mockFetch.mockReset();
});

describe('notification proxy routes', () => {
  describe('GET /api/notifications/prefs', () => {
    it('proxies prefs request with userId', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ email: true, push: false, inApp: true }),
      });

      const res = await app.inject({ method: 'GET', url: '/api/notifications/prefs' });

      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.payload);
      expect(body.email).toBe(true);
      const fetchUrl = mockFetch.mock.calls[0][0] as string;
      expect(fetchUrl).toContain('notifications/prefs/demo-user');
    });

    it('returns 502 when platform-service is unreachable', async () => {
      mockFetch.mockRejectedValue(new Error('ECONNREFUSED'));

      const res = await app.inject({ method: 'GET', url: '/api/notifications/prefs' });

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

  describe('PUT /api/notifications/prefs', () => {
    it('proxies prefs update with body', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ email: false, push: true, inApp: true }),
      });

      const res = await app.inject({
        method: 'PUT',
        url: '/api/notifications/prefs',
        payload: { email: false, push: true },
      });

      expect(res.statusCode).toBe(200);
      expect(mockFetch.mock.calls[0][1].method).toBe('PUT');
    });
  });

  describe('GET /api/webhooks', () => {
    it('lists webhook subscriptions', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ subscriptions: [{ id: 'wh_1', url: 'https://example.com' }] }),
      });

      const res = await app.inject({ method: 'GET', url: '/api/webhooks' });

      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.payload);
      expect(body.subscriptions).toHaveLength(1);
    });

    it('returns 502 when platform-service is unreachable', async () => {
      mockFetch.mockRejectedValue(new Error('ECONNREFUSED'));

      const res = await app.inject({ method: 'GET', url: '/api/webhooks' });

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

  describe('POST /api/webhooks', () => {
    it('creates a webhook subscription', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ id: 'wh_new', url: 'https://example.com/hook' }),
      });

      const res = await app.inject({
        method: 'POST',
        url: '/api/webhooks',
        payload: { url: 'https://example.com/hook', events: ['task.completed'] },
      });

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

  describe('DELETE /api/webhooks/:id', () => {
    it('deletes a webhook subscription', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ success: true }),
      });

      const res = await app.inject({ method: 'DELETE', url: '/api/webhooks/wh_1' });

      expect(res.statusCode).toBe(200);
      const fetchUrl = mockFetch.mock.calls[0][0] as string;
      expect(fetchUrl).toContain('webhooks/subscriptions/wh_1');
    });

    it('returns 502 when platform-service is unreachable', async () => {
      mockFetch.mockRejectedValue(new Error('ECONNREFUSED'));

      const res = await app.inject({ method: 'DELETE', url: '/api/webhooks/wh_1' });

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