// Mock dependencies
jest.mock('../../../../../services/email/core/EmailClient', () => {
  return jest.fn().mockImplementation(() => ({
    initialize: jest.fn().mockResolvedValue(),
    sendEmail: jest.fn().mockResolvedValue({ success: true, messageId: 'msg-123' }),
  }));
});

jest.mock('../../../../../services/email/core/TemplateManager', () => {
  return jest.fn().mockImplementation(() => ({
    initialize: jest.fn().mockResolvedValue(),
    renderTemplate: jest.fn().mockResolvedValue('<html>Test</html>'),
  }));
});

jest.mock('../../../../../utils/logger', () => ({
  info: jest.fn(),
  error: jest.fn(),
  warn: jest.fn(),
}));

const PaymentEmailService = require('../../../../../services/email/domain/PaymentEmailService');

describe('PaymentEmailService', () => {
  let service;
  const originalEnv = process.env;

  beforeEach(() => {
    jest.clearAllMocks();
    process.env = {
      ...originalEnv,
      FRONTEND_URL: 'http://localhost:3000',
      ADMIN_EMAIL: 'admin@example.com',
    };
    service = new PaymentEmailService();
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  describe('initialize', () => {
    it('should initialize only once', async () => {
      await service.initialize();
      await service.initialize();

      expect(service.emailClient.initialize).toHaveBeenCalledTimes(1);
    });
  });

  describe('sendPaymentDeclinedNotification', () => {
    it('should send payment declined notification to renter', async () => {
      const result = await service.sendPaymentDeclinedNotification('renter@example.com', {
        renterFirstName: 'John',
        itemName: 'Test Item',
        declineReason: 'Card declined',
        updatePaymentUrl: 'http://localhost:3000/update-payment',
      });

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'paymentDeclinedToRenter',
        expect.objectContaining({
          renterFirstName: 'John',
          itemName: 'Test Item',
          declineReason: 'Card declined',
        })
      );
    });

    it('should use default values for missing params', async () => {
      await service.sendPaymentDeclinedNotification('renter@example.com', {});

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'paymentDeclinedToRenter',
        expect.objectContaining({
          renterFirstName: 'there',
          itemName: 'the item',
        })
      );
    });

    it('should handle errors gracefully', async () => {
      service.templateManager.renderTemplate.mockRejectedValue(new Error('Template error'));

      const result = await service.sendPaymentDeclinedNotification('test@example.com', {});

      expect(result.success).toBe(false);
      expect(result.error).toContain('Template error');
    });
  });

  describe('sendPaymentMethodUpdatedNotification', () => {
    it('should send payment method updated notification to owner', async () => {
      const result = await service.sendPaymentMethodUpdatedNotification('owner@example.com', {
        ownerFirstName: 'Jane',
        itemName: 'Test Item',
        approvalUrl: 'http://localhost:3000/approve',
      });

      expect(result.success).toBe(true);
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'owner@example.com',
        'Payment Method Updated - Test Item',
        expect.any(String)
      );
    });
  });

  describe('sendPayoutFailedNotification', () => {
    it('should send payout failed notification to owner', async () => {
      const result = await service.sendPayoutFailedNotification('owner@example.com', {
        ownerName: 'John',
        payoutAmount: 50.00,
        failureMessage: 'Bank account closed',
        actionRequired: 'Please update your bank account',
        failureCode: 'account_closed',
        requiresBankUpdate: true,
      });

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'payoutFailedToOwner',
        expect.objectContaining({
          ownerName: 'John',
          payoutAmount: '50.00',
          failureCode: 'account_closed',
          requiresBankUpdate: true,
        })
      );
    });
  });

  describe('sendAccountDisconnectedEmail', () => {
    it('should send account disconnected notification', async () => {
      const result = await service.sendAccountDisconnectedEmail('owner@example.com', {
        ownerName: 'John',
        hasPendingPayouts: true,
        pendingPayoutCount: 3,
      });

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'accountDisconnectedToOwner',
        expect.objectContaining({
          hasPendingPayouts: true,
          pendingPayoutCount: 3,
        })
      );
    });

    it('should use default values for missing params', async () => {
      await service.sendAccountDisconnectedEmail('owner@example.com', {});

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'accountDisconnectedToOwner',
        expect.objectContaining({
          ownerName: 'there',
          hasPendingPayouts: false,
          pendingPayoutCount: 0,
        })
      );
    });
  });

  describe('sendPayoutsDisabledEmail', () => {
    it('should send payouts disabled notification', async () => {
      const result = await service.sendPayoutsDisabledEmail('owner@example.com', {
        ownerName: 'John',
        disabledReason: 'Verification required',
      });

      expect(result.success).toBe(true);
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'owner@example.com',
        'Action Required: Your payouts have been paused - Village Share',
        expect.any(String)
      );
    });
  });

  describe('sendDisputeAlertEmail', () => {
    it('should send dispute alert to admin', async () => {
      const result = await service.sendDisputeAlertEmail({
        rentalId: 'rental-123',
        amount: 50.00,
        reason: 'fraudulent',
        evidenceDueBy: new Date(),
        renterName: 'Renter Name',
        renterEmail: 'renter@example.com',
        ownerName: 'Owner Name',
        ownerEmail: 'owner@example.com',
        itemName: 'Test Item',
      });

      expect(result.success).toBe(true);
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'admin@example.com',
        'URGENT: Payment Dispute - Rental #rental-123',
        expect.any(String)
      );
    });
  });

  describe('sendDisputeLostAlertEmail', () => {
    it('should send dispute lost alert to admin', async () => {
      const result = await service.sendDisputeLostAlertEmail({
        rentalId: 'rental-123',
        amount: 50.00,
        ownerPayoutAmount: 45.00,
        ownerName: 'Owner Name',
        ownerEmail: 'owner@example.com',
      });

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'disputeLostAlertToAdmin',
        expect.objectContaining({
          rentalId: 'rental-123',
          amount: '50.00',
          ownerPayoutAmount: '45.00',
        })
      );
    });
  });

  describe('formatDisputeReason', () => {
    it('should format known dispute reasons', () => {
      expect(service.formatDisputeReason('fraudulent')).toBe('Fraudulent transaction');
      expect(service.formatDisputeReason('product_not_received')).toBe('Product not received');
      expect(service.formatDisputeReason('duplicate')).toBe('Duplicate charge');
    });

    it('should return original reason for unknown reasons', () => {
      expect(service.formatDisputeReason('unknown_reason')).toBe('unknown_reason');
    });

    it('should return "Unknown reason" for null/undefined', () => {
      expect(service.formatDisputeReason(null)).toBe('Unknown reason');
      expect(service.formatDisputeReason(undefined)).toBe('Unknown reason');
    });
  });
});
