/**
 * Route-level tests for referrals module — Fastify inject.
 */

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

const repoMock = {
  listAll: vi.fn(),
  countReferrals: vi.fn(),
  getByReferrer: vi.fn(),
  getByReferredEmail: vi.fn(),
  create: vi.fn(),
  update: vi.fn(),
};

const webhookMock = {
  dispatchReferralStatusChanged: vi.fn(),
};

vi.mock('./repository.js', () => repoMock);
vi.mock('../../lib/webhooks.js', () => webhookMock);
vi.mock('../../lib/request-context.js', () => ({
  getRequestProductId: () => 'lysnrai',
}));

const baseReferral = {
  id: 'ref_1',
  productId: 'lysnrai',
  referrerId: 'user_1',
  referrerEmail: 'referrer@example.com',
  referredUserId: null,
  referredEmail: 'friend@example.com',
  status: 'pending',
  referrerRewardTokens: 1000,
  referredRewardTokens: 500,
  referrerRewarded: false,
  referredRewarded: false,
  createdAt: '2026-02-16T00:00:00Z',
  completedAt: null,
};

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

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('GET /referrals lists referrals', async () => {
    repoMock.listAll.mockResolvedValue([baseReferral]);

    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

    const res = await app.inject({ method: 'GET', url: '/api/referrals?limit=10&offset=0' });

    expect(res.statusCode).toBe(200);
    const data = JSON.parse(res.body);
    expect(data.referrals).toHaveLength(1);
    expect(data.count).toBe(1);
  });

  it('GET /referrals/stats returns counters', async () => {
    repoMock.countReferrals.mockResolvedValue({ total: 10, completed: 4, rewarded: 2 });

    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

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

    expect(res.statusCode).toBe(200);
    const data = JSON.parse(res.body);
    expect(data.total).toBe(10);
  });

  it('GET /referrals/by-email/:email returns 404 when missing', async () => {
    repoMock.getByReferredEmail.mockResolvedValue(null);

    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

    const res = await app.inject({ method: 'GET', url: '/api/referrals/by-email/missing@example.com' });

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

  it('POST /referrals creates referral when email not existing', async () => {
    repoMock.getByReferredEmail.mockResolvedValue(null);
    repoMock.create.mockResolvedValue(baseReferral);

    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

    const res = await app.inject({
      method: 'POST',
      url: '/api/referrals',
      payload: {
        referrerId: 'user_1',
        referrerEmail: 'referrer@example.com',
        referredEmail: 'friend@example.com',
        referrerRewardTokens: 1000,
        referredRewardTokens: 500,
      },
    });

    expect(res.statusCode).toBe(201);
    const data = JSON.parse(res.body);
    expect(data.id).toBe('ref_1');
  });

  it('POST /referrals returns 400 for duplicate referred email', async () => {
    repoMock.getByReferredEmail.mockResolvedValue(baseReferral);

    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

    const res = await app.inject({
      method: 'POST',
      url: '/api/referrals',
      payload: {
        referrerId: 'user_1',
        referrerEmail: 'referrer@example.com',
        referredEmail: 'friend@example.com',
      },
    });

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

  it('PUT /referrals/:id requires referrerId query param', async () => {
    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

    const res = await app.inject({
      method: 'PUT',
      url: '/api/referrals/ref_1',
      payload: { status: 'signed_up' },
    });

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

  it('PUT /referrals/:id updates referral and dispatches webhook', async () => {
    repoMock.update.mockResolvedValue({
      ...baseReferral,
      status: 'signed_up',
      referredUserId: 'user_2',
      completedAt: '2026-02-16T01:00:00Z',
    });

    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

    const res = await app.inject({
      method: 'PUT',
      url: '/api/referrals/ref_1?referrerId=user_1',
      payload: { status: 'signed_up', referredUserId: 'user_2' },
    });

    expect(res.statusCode).toBe(200);
    const data = JSON.parse(res.body);
    expect(data.status).toBe('signed_up');
    expect(webhookMock.dispatchReferralStatusChanged).toHaveBeenCalled();
  });

  it('PUT /referrals/:id returns 404 when update returns null', async () => {
    repoMock.update.mockResolvedValue(null);

    const { referralRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(referralRoutes, { prefix: '/api' });

    const res = await app.inject({
      method: 'PUT',
      url: '/api/referrals/ref_1?referrerId=user_1',
      payload: { status: 'signed_up' },
    });

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