/**
 * Tests for transactional email send functions.
 *
 * Verifies that each send function:
 *  - calls Resend with the correct from/to/subject/html/text
 *  - falls back gracefully when RESEND_API_KEY is unset (dev mode)
 *  - propagates Resend API errors
 */

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

// ---- Resend mock ----
const mockSend = vi.fn();

// We need a proper class constructor mock because `send.ts` does `new Resend(...)`.
class MockResend {
  emails = { send: mockSend };
}

vi.mock('resend', () => ({
  Resend: MockResend,
}));

// Dynamically import to allow vi.resetModules to re-evaluate the module
// (the top-level `const resend = ...` runs at import time).
async function importSendModule() {
  vi.resetModules();

  // Re-apply mock after resetModules clears the mock registry
  vi.doMock('resend', () => ({
    Resend: MockResend,
  }));

  return import('../send');
}

// ---------------------------------------------------------------------------
// Test suite -- Resend configured (production-like)
// ---------------------------------------------------------------------------
describe('email send functions (Resend configured)', () => {
  beforeEach(() => {
    vi.stubEnv('RESEND_API_KEY', 're_test_123456');
    vi.stubEnv('NEXTAUTH_URL', 'https://llmhosts.com');
    mockSend.mockReset();
    mockSend.mockResolvedValue({ data: { id: 'msg_abc123' }, error: null });
  });

  // -- sendWelcomeEmail -------------------------------------------------
  describe('sendWelcomeEmail', () => {
    it('sends a welcome email with correct subject and content', async () => {
      const { sendWelcomeEmail } = await importSendModule();

      const result = await sendWelcomeEmail('alice@example.com', 'Alice');

      expect(result).toEqual({ success: true, messageId: 'msg_abc123' });
      expect(mockSend).toHaveBeenCalledOnce();

      const call = mockSend.mock.calls[0][0];
      expect(call.to).toBe('alice@example.com');
      expect(call.subject).toBe('Welcome to LLMHosts!');
      expect(call.html).toContain('Welcome to LLMHosts, Alice!');
      expect(call.html).toContain('Get Started');
      expect(call.html).toContain('/dashboard');
      expect(call.text).toContain('Welcome to LLMHosts, Alice!');
      expect(call.text).toContain('/dashboard');
    });
  });

  // -- sendTeamInviteEmail -----------------------------------------------
  describe('sendTeamInviteEmail', () => {
    it('sends a team invite email with correct subject and content', async () => {
      const { sendTeamInviteEmail } = await importSendModule();

      const result = await sendTeamInviteEmail(
        'bob@example.com',
        'Alice',
        'Acme Corp',
        'Member',
        'https://llmhosts.com/invite/abc123'
      );

      expect(result).toEqual({ success: true, messageId: 'msg_abc123' });
      expect(mockSend).toHaveBeenCalledOnce();

      const call = mockSend.mock.calls[0][0];
      expect(call.to).toBe('bob@example.com');
      expect(call.subject).toBe('Alice invited you to join Acme Corp on LLMHosts');
      expect(call.html).toContain('Alice');
      expect(call.html).toContain('Acme Corp');
      expect(call.html).toContain('Member');
      expect(call.html).toContain('Accept Invitation');
      expect(call.html).toContain('https://llmhosts.com/invite/abc123');
      expect(call.text).toContain('Alice');
      expect(call.text).toContain('Acme Corp');
      expect(call.text).toContain('Member');
      expect(call.text).toContain('https://llmhosts.com/invite/abc123');
    });
  });

  // -- sendPaymentFailedEmail -------------------------------------------
  describe('sendPaymentFailedEmail', () => {
    it('sends a payment failed email with correct subject and content', async () => {
      const { sendPaymentFailedEmail } = await importSendModule();

      const result = await sendPaymentFailedEmail('charlie@example.com', 'Charlie');

      expect(result).toEqual({ success: true, messageId: 'msg_abc123' });
      expect(mockSend).toHaveBeenCalledOnce();

      const call = mockSend.mock.calls[0][0];
      expect(call.to).toBe('charlie@example.com');
      expect(call.subject).toBe('Action required: Payment failed for your LLMHosts subscription');
      expect(call.html).toContain('Charlie');
      expect(call.html).toContain('Update Payment Method');
      expect(call.html).toContain('/settings/billing');
      expect(call.text).toContain('Charlie');
      expect(call.text).toContain('/settings/billing');
    });
  });

  // -- sendSubscriptionConfirmedEmail -----------------------------------
  describe('sendSubscriptionConfirmedEmail', () => {
    it('sends a subscription confirmed email with correct subject and content', async () => {
      const { sendSubscriptionConfirmedEmail } = await importSendModule();

      const features = ['Unlimited models', 'Team management', 'Priority support'];
      const result = await sendSubscriptionConfirmedEmail('dana@example.com', 'Dana', 'Pro', features);

      expect(result).toEqual({ success: true, messageId: 'msg_abc123' });
      expect(mockSend).toHaveBeenCalledOnce();

      const call = mockSend.mock.calls[0][0];
      expect(call.to).toBe('dana@example.com');
      expect(call.subject).toBe('Your LLMHosts Pro plan is active!');
      expect(call.html).toContain('Dana');
      expect(call.html).toContain('Pro');
      expect(call.html).toContain('Unlimited models');
      expect(call.html).toContain('Team management');
      expect(call.html).toContain('Priority support');
      expect(call.html).toContain('Go to Dashboard');
      expect(call.text).toContain('Dana');
      expect(call.text).toContain('Pro');
      expect(call.text).toContain('Unlimited models');
      expect(call.text).toContain('Team management');
      expect(call.text).toContain('Priority support');
    });
  });

  // -- sendSpendingAlertEmail -------------------------------------------
  describe('sendSpendingAlertEmail', () => {
    it('sends a spending alert email with correct subject and content', async () => {
      const { sendSpendingAlertEmail } = await importSendModule();

      const result = await sendSpendingAlertEmail('admin@example.com', 'Acme Corp', 85.5, 100.0, 86);

      expect(result).toEqual({ success: true, messageId: 'msg_abc123' });
      expect(mockSend).toHaveBeenCalledOnce();

      const call = mockSend.mock.calls[0][0];
      expect(call.to).toBe('admin@example.com');
      expect(call.subject).toBe('Spending alert: Acme Corp has reached 86% of budget');
      expect(call.html).toContain('Acme Corp');
      expect(call.html).toContain('$85.50');
      expect(call.html).toContain('$100.00');
      expect(call.html).toContain('86%');
      expect(call.html).toContain('View Usage');
      expect(call.html).toContain('/dashboard/usage');
      expect(call.text).toContain('Acme Corp');
      expect(call.text).toContain('$85.50');
      expect(call.text).toContain('$100.00');
      expect(call.text).toContain('86%');
    });
  });

  // -- error propagation ------------------------------------------------
  describe('error handling', () => {
    it('throws when Resend returns an API error', async () => {
      mockSend.mockResolvedValue({ data: null, error: { message: 'Rate limit exceeded' } });
      const { sendWelcomeEmail } = await importSendModule();

      await expect(sendWelcomeEmail('fail@example.com', 'Fail')).rejects.toThrow(
        'Email sending failed: Rate limit exceeded'
      );
    });

    it('throws when Resend SDK throws', async () => {
      mockSend.mockRejectedValue(new Error('Network error'));
      const { sendTeamInviteEmail } = await importSendModule();

      await expect(
        sendTeamInviteEmail('fail@example.com', 'Alice', 'Team', 'Member', 'https://example.com/invite')
      ).rejects.toThrow('Network error');
    });
  });
});

// ---------------------------------------------------------------------------
// Test suite -- Resend NOT configured (dev mode fallback)
// ---------------------------------------------------------------------------
describe('email send functions (Resend NOT configured / dev mode)', () => {
  beforeEach(() => {
    vi.stubEnv('RESEND_API_KEY', '');
    vi.stubEnv('NEXTAUTH_URL', 'https://llmhosts.com');
    mockSend.mockReset();
  });

  it('sendWelcomeEmail returns dev-mode without calling Resend', async () => {
    const { sendWelcomeEmail } = await importSendModule();

    const result = await sendWelcomeEmail('alice@example.com', 'Alice');

    expect(result).toEqual({ success: true, messageId: 'dev-mode' });
    expect(mockSend).not.toHaveBeenCalled();
  });

  it('sendTeamInviteEmail returns dev-mode without calling Resend', async () => {
    const { sendTeamInviteEmail } = await importSendModule();

    const result = await sendTeamInviteEmail(
      'bob@example.com',
      'Alice',
      'Acme Corp',
      'Member',
      'https://llmhosts.com/invite/abc123'
    );

    expect(result).toEqual({ success: true, messageId: 'dev-mode' });
    expect(mockSend).not.toHaveBeenCalled();
  });

  it('sendPaymentFailedEmail returns dev-mode without calling Resend', async () => {
    const { sendPaymentFailedEmail } = await importSendModule();

    const result = await sendPaymentFailedEmail('charlie@example.com', 'Charlie');

    expect(result).toEqual({ success: true, messageId: 'dev-mode' });
    expect(mockSend).not.toHaveBeenCalled();
  });

  it('sendSubscriptionConfirmedEmail returns dev-mode without calling Resend', async () => {
    const { sendSubscriptionConfirmedEmail } = await importSendModule();

    const result = await sendSubscriptionConfirmedEmail('dana@example.com', 'Dana', 'Pro', ['Feature A']);

    expect(result).toEqual({ success: true, messageId: 'dev-mode' });
    expect(mockSend).not.toHaveBeenCalled();
  });

  it('sendSpendingAlertEmail returns dev-mode without calling Resend', async () => {
    const { sendSpendingAlertEmail } = await importSendModule();

    const result = await sendSpendingAlertEmail('admin@example.com', 'Acme Corp', 50.0, 100.0, 50);

    expect(result).toEqual({ success: true, messageId: 'dev-mode' });
    expect(mockSend).not.toHaveBeenCalled();
  });
});
