// Mock dependencies before requiring the service
jest.mock('../../../models', () => ({
  Rental: {
    findOne: jest.fn(),
  },
  User: {},
  Item: {},
}));

jest.mock('../../../services/email', () => ({
  payment: {
    sendDisputeAlertEmail: jest.fn(),
    sendDisputeLostAlertEmail: jest.fn(),
  },
}));

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

const { Rental } = require('../../../models');
const emailServices = require('../../../services/email');
const DisputeService = require('../../../services/disputeService');

describe('DisputeService', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('handleDisputeCreated', () => {
    const mockDispute = {
      id: 'dp_123',
      payment_intent: 'pi_456',
      reason: 'fraudulent',
      amount: 5000,
      created: Math.floor(Date.now() / 1000),
      evidence_details: {
        due_by: Math.floor(Date.now() / 1000) + 86400 * 7,
      },
    };

    it('should process dispute and update rental', async () => {
      const mockRental = {
        id: 'rental-123',
        bankDepositStatus: 'pending',
        owner: { email: 'owner@test.com', firstName: 'Owner' },
        renter: { email: 'renter@test.com', firstName: 'Renter' },
        item: { name: 'Test Item' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);
      emailServices.payment.sendDisputeAlertEmail.mockResolvedValue();

      const result = await DisputeService.handleDisputeCreated(mockDispute);

      expect(result.processed).toBe(true);
      expect(result.rentalId).toBe('rental-123');
      expect(mockRental.update).toHaveBeenCalledWith(
        expect.objectContaining({
          stripeDisputeId: 'dp_123',
          stripeDisputeReason: 'fraudulent',
          stripeDisputeAmount: 5000,
        })
      );
    });

    it('should put payout on hold if not yet deposited', async () => {
      const mockRental = {
        id: 'rental-123',
        bankDepositStatus: 'pending',
        owner: { email: 'owner@test.com' },
        renter: { email: 'renter@test.com' },
        item: { name: 'Test Item' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);
      emailServices.payment.sendDisputeAlertEmail.mockResolvedValue();

      await DisputeService.handleDisputeCreated(mockDispute);

      expect(mockRental.update).toHaveBeenCalledWith({ payoutStatus: 'on_hold' });
    });

    it('should not put payout on hold if already deposited', async () => {
      const mockRental = {
        id: 'rental-123',
        bankDepositStatus: 'paid',
        owner: { email: 'owner@test.com' },
        renter: { email: 'renter@test.com' },
        item: { name: 'Test Item' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);
      emailServices.payment.sendDisputeAlertEmail.mockResolvedValue();

      await DisputeService.handleDisputeCreated(mockDispute);

      // Should be called once for dispute info, not for on_hold
      const updateCalls = mockRental.update.mock.calls;
      const onHoldCall = updateCalls.find(call => call[0].payoutStatus === 'on_hold');
      expect(onHoldCall).toBeUndefined();
    });

    it('should send dispute alert email', async () => {
      const mockRental = {
        id: 'rental-123',
        bankDepositStatus: 'pending',
        owner: { email: 'owner@test.com', firstName: 'Owner' },
        renter: { email: 'renter@test.com', firstName: 'Renter' },
        item: { name: 'Test Item' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);
      emailServices.payment.sendDisputeAlertEmail.mockResolvedValue();

      await DisputeService.handleDisputeCreated(mockDispute);

      expect(emailServices.payment.sendDisputeAlertEmail).toHaveBeenCalledWith(
        expect.objectContaining({
          rentalId: 'rental-123',
          amount: 50, // Converted from cents
          reason: 'fraudulent',
          renterEmail: 'renter@test.com',
          ownerEmail: 'owner@test.com',
        })
      );
    });

    it('should return not processed when rental not found', async () => {
      Rental.findOne.mockResolvedValue(null);

      const result = await DisputeService.handleDisputeCreated(mockDispute);

      expect(result.processed).toBe(false);
      expect(result.reason).toBe('rental_not_found');
    });
  });

  describe('handleDisputeClosed', () => {
    it('should process won dispute and resume payout', async () => {
      const mockRental = {
        id: 'rental-123',
        payoutStatus: 'on_hold',
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);

      const mockDispute = {
        id: 'dp_123',
        status: 'won',
        amount: 5000,
      };

      const result = await DisputeService.handleDisputeClosed(mockDispute);

      expect(result.processed).toBe(true);
      expect(result.won).toBe(true);
      expect(mockRental.update).toHaveBeenCalledWith({ payoutStatus: 'pending' });
    });

    it('should process lost dispute and record loss', async () => {
      const mockRental = {
        id: 'rental-123',
        payoutStatus: 'on_hold',
        bankDepositStatus: 'pending',
        owner: { email: 'owner@test.com' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);

      const mockDispute = {
        id: 'dp_123',
        status: 'lost',
        amount: 5000,
      };

      const result = await DisputeService.handleDisputeClosed(mockDispute);

      expect(result.processed).toBe(true);
      expect(result.won).toBe(false);
      expect(mockRental.update).toHaveBeenCalledWith({
        stripeDisputeLost: true,
        stripeDisputeLostAmount: 5000,
      });
    });

    it('should send alert when dispute lost and owner already paid', async () => {
      const mockRental = {
        id: 'rental-123',
        payoutStatus: 'on_hold',
        bankDepositStatus: 'paid',
        payoutAmount: 4500,
        owner: { email: 'owner@test.com', firstName: 'Owner' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);
      emailServices.payment.sendDisputeLostAlertEmail.mockResolvedValue();

      const mockDispute = {
        id: 'dp_123',
        status: 'lost',
        amount: 5000,
      };

      await DisputeService.handleDisputeClosed(mockDispute);

      expect(emailServices.payment.sendDisputeLostAlertEmail).toHaveBeenCalledWith(
        expect.objectContaining({
          rentalId: 'rental-123',
          ownerAlreadyPaid: true,
          ownerPayoutAmount: 4500,
        })
      );
    });

    it('should not send alert when dispute lost but owner not yet paid', async () => {
      const mockRental = {
        id: 'rental-123',
        payoutStatus: 'on_hold',
        bankDepositStatus: 'pending',
        owner: { email: 'owner@test.com' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);

      const mockDispute = {
        id: 'dp_123',
        status: 'lost',
        amount: 5000,
      };

      await DisputeService.handleDisputeClosed(mockDispute);

      expect(emailServices.payment.sendDisputeLostAlertEmail).not.toHaveBeenCalled();
    });

    it('should return not processed when rental not found', async () => {
      Rental.findOne.mockResolvedValue(null);

      const mockDispute = {
        id: 'dp_123',
        status: 'won',
      };

      const result = await DisputeService.handleDisputeClosed(mockDispute);

      expect(result.processed).toBe(false);
      expect(result.reason).toBe('rental_not_found');
    });

    it('should handle warning_closed status as not won', async () => {
      const mockRental = {
        id: 'rental-123',
        payoutStatus: 'pending',
        bankDepositStatus: 'pending',
        owner: { email: 'owner@test.com' },
        update: jest.fn().mockResolvedValue(),
      };

      Rental.findOne.mockResolvedValue(mockRental);

      const mockDispute = {
        id: 'dp_123',
        status: 'warning_closed',
        amount: 5000,
      };

      const result = await DisputeService.handleDisputeClosed(mockDispute);

      expect(result.won).toBe(false);
    });
  });
});
