/**
 * Renting Page Tests
 *
 * Tests for the renter dashboard page showing rentals,
 * status management, and condition checks.
 */

import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { vi, type MockedFunction } from 'vitest';
import { MemoryRouter, Routes, Route } from 'react-router';
import Renting from '../../pages/Renting';
import { useAuth } from '../../contexts/AuthContext';
import { rentalAPI, conditionCheckAPI } from '../../services/api';
import {
  mockUser,
  mockItemWithOwner,
  createMockRental,
  createMockConditionCheck,
  createMockAvailableChecks,
} from '../../mocks/handlers';

// Mock dependencies
vi.mock('../../contexts/AuthContext');
vi.mock('../../services/api', () => ({
  rentalAPI: {
    getRentals: vi.fn(),
  },
  conditionCheckAPI: {
    getAvailableChecks: vi.fn(),
    getBatchConditionChecks: vi.fn(),
  },
}));

vi.mock('../../services/uploadService', () => ({
  getImageUrl: vi.fn((filename, variant) => `https://test-bucket.s3.amazonaws.com/${variant}/${filename}`),
}));

// Mock modal components
vi.mock('../../components/ReviewModal', () => ({
  default: ({ show, onClose, rental }: any) =>
    show ? (
      <div data-testid="review-modal">
        <span>Review {rental?.item?.name}</span>
        <button onClick={onClose}>Close</button>
      </div>
    ) : null,
}));

vi.mock('../../components/RentalCancellationModal', () => ({
  default: ({ show, onHide, rental, onCancellationComplete }: any) =>
    show ? (
      <div data-testid="cancellation-modal">
        <span>Cancel {rental?.item?.name}</span>
        <button onClick={onHide}>Close</button>
        <button onClick={() => onCancellationComplete({ ...rental, status: 'cancelled' })}>
          Confirm Cancel
        </button>
      </div>
    ) : null,
}));

vi.mock('../../components/ConditionCheckModal', () => ({
  default: ({ show, onHide, rentalId, checkType, onSuccess }: any) =>
    show ? (
      <div data-testid="condition-check-modal">
        <span>Condition Check for rental {rentalId}</span>
        <span>Type: {checkType}</span>
        <button onClick={onHide}>Close</button>
        <button onClick={onSuccess}>Submit</button>
      </div>
    ) : null,
}));

vi.mock('../../components/ConditionCheckViewerModal', () => ({
  default: ({ show, onHide, conditionCheck }: any) =>
    show ? (
      <div data-testid="condition-viewer-modal">
        <span>View Check: {conditionCheck?.checkType}</span>
        <button onClick={onHide}>Close</button>
      </div>
    ) : null,
}));

vi.mock('../../components/UpdatePaymentMethodModal', () => ({
  default: ({ show, onHide, rentalId, onSuccess }: any) =>
    show ? (
      <div data-testid="update-payment-modal">
        <span>Update Payment for {rentalId}</span>
        <button onClick={onHide}>Close</button>
        <button onClick={onSuccess}>Update</button>
      </div>
    ) : null,
}));

const mockNavigate = vi.fn();
vi.mock('react-router', async (importOriginal) => {
  const actual = await importOriginal<typeof import('react-router')>();
  return {
    ...actual,
    useNavigate: () => mockNavigate,
  };
});

const mockedUseAuth = useAuth as MockedFunction<typeof useAuth>;
const mockedGetRentals = rentalAPI.getRentals as MockedFunction<typeof rentalAPI.getRentals>;
const mockedGetAvailableChecks = conditionCheckAPI.getAvailableChecks as MockedFunction<typeof conditionCheckAPI.getAvailableChecks>;
const mockedGetBatchConditionChecks = conditionCheckAPI.getBatchConditionChecks as MockedFunction<typeof conditionCheckAPI.getBatchConditionChecks>;

// Helper to render Renting page
const renderRenting = (authOverrides: Partial<ReturnType<typeof useAuth>> = {}) => {
  mockedUseAuth.mockReturnValue({
    user: mockUser,
    loading: false,
    showAuthModal: false,
    authModalMode: 'login',
    login: vi.fn(),
    register: vi.fn(),
    googleLogin: vi.fn(),
    logout: vi.fn(),
    checkAuth: vi.fn(),
    updateUser: vi.fn(),
    openAuthModal: vi.fn(),
    closeAuthModal: vi.fn(),
    ...authOverrides,
  });

  return render(
    <MemoryRouter initialEntries={['/renting']}>
      <Routes>
        <Route path="/renting" element={<Renting />} />
      </Routes>
    </MemoryRouter>
  );
};

describe('Renting', () => {
  const mockOwner = {
    id: '2',
    firstName: 'Owner',
    lastName: 'User',
    email: 'owner@example.com',
  };

  const mockRentals = [
    createMockRental({
      id: '1',
      status: 'pending',
      displayStatus: 'pending',
      item: mockItemWithOwner as any,
      owner: mockOwner as any,
    }),
    createMockRental({
      id: '2',
      status: 'confirmed',
      displayStatus: 'confirmed',
      item: { ...mockItemWithOwner, name: 'Mountain Bike' } as any,
      owner: mockOwner as any,
    }),
    createMockRental({
      id: '3',
      status: 'confirmed',
      displayStatus: 'active',
      item: { ...mockItemWithOwner, name: 'Kayak' } as any,
      owner: mockOwner as any,
    }),
  ];

  beforeEach(() => {
    vi.clearAllMocks();
    mockedGetRentals.mockResolvedValue({ data: mockRentals });
    mockedGetAvailableChecks.mockResolvedValue({ data: createMockAvailableChecks('1', []) });
    mockedGetBatchConditionChecks.mockResolvedValue({ data: { conditionChecks: [] } });
  });

  describe('Loading State', () => {
    it('shows loading spinner while fetching rentals', () => {
      mockedGetRentals.mockImplementation(() => new Promise(() => {}));
      renderRenting();

      expect(screen.getByRole('status')).toBeInTheDocument();
    });
  });

  describe('Fetching and Displaying Rentals', () => {
    it('fetches user rentals as renter', async () => {
      renderRenting();

      await waitFor(() => {
        expect(mockedGetRentals).toHaveBeenCalled();
      });
    });

    it('displays rentals in cards', async () => {
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
        expect(screen.getByText('Mountain Bike')).toBeInTheDocument();
        expect(screen.getByText('Kayak')).toBeInTheDocument();
      });
    });

    it('displays rental details (dates, amount, owner)', async () => {
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      // Check total amounts are displayed
      expect(screen.getAllByText(/\$25/)).toHaveLength(3);
    });

    it('shows rental status badges', async () => {
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Awaiting Owner Approval')).toBeInTheDocument();
        expect(screen.getByText('Confirmed & Paid')).toBeInTheDocument();
        expect(screen.getByText('Active')).toBeInTheDocument();
      });
    });
  });

  describe('Empty State', () => {
    it('shows empty state when no rentals', async () => {
      mockedGetRentals.mockResolvedValue({ data: [] });
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('No Active Rental Requests')).toBeInTheDocument();
      });

      expect(screen.getByRole('link', { name: /browse items to rent/i })).toBeInTheDocument();
    });
  });

  describe('Error Handling', () => {
    it('displays error message on API failure', async () => {
      mockedGetRentals.mockRejectedValue({
        response: { data: { message: 'Failed to fetch rentals' } },
      });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByRole('alert')).toBeInTheDocument();
        expect(screen.getByText('Failed to fetch rentals')).toBeInTheDocument();
      });
    });
  });

  describe('Rental Actions', () => {
    it('shows cancel button for pending rentals', async () => {
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      // Find cancel buttons
      const cancelButtons = screen.getAllByRole('button', { name: /cancel/i });
      expect(cancelButtons.length).toBeGreaterThan(0);
    });

    it('opens cancellation modal on cancel click', async () => {
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      const cancelButtons = screen.getAllByRole('button', { name: /cancel/i });
      fireEvent.click(cancelButtons[0]);

      expect(screen.getByTestId('cancellation-modal')).toBeInTheDocument();
    });

    it('shows review button for active rentals', async () => {
      const activeRental = createMockRental({
        id: '1',
        status: 'confirmed',
        displayStatus: 'active',
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [activeRental] });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      expect(screen.getByRole('button', { name: /review/i })).toBeInTheDocument();
    });

    it('opens review modal on review click', async () => {
      const activeRental = createMockRental({
        id: '1',
        status: 'confirmed',
        displayStatus: 'active',
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [activeRental] });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      const reviewButton = screen.getByRole('button', { name: /review/i });
      fireEvent.click(reviewButton);

      expect(screen.getByTestId('review-modal')).toBeInTheDocument();
    });
  });

  describe('Payment Status Alerts', () => {
    it('shows Complete Payment button for requires_action status', async () => {
      const requiresActionRental = createMockRental({
        id: '1',
        status: 'pending',
        displayStatus: 'pending',
        paymentStatus: 'requires_action',
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [requiresActionRental] });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      expect(screen.getByRole('button', { name: /complete payment/i })).toBeInTheDocument();
      expect(screen.getByText(/payment authentication required/i)).toBeInTheDocument();
    });

    it('navigates to complete payment on button click', async () => {
      const requiresActionRental = createMockRental({
        id: '1',
        status: 'pending',
        displayStatus: 'pending',
        paymentStatus: 'requires_action',
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [requiresActionRental] });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      const completePaymentButton = screen.getByRole('button', { name: /complete payment/i });
      fireEvent.click(completePaymentButton);

      expect(mockNavigate).toHaveBeenCalledWith('/complete-payment/1');
    });

    it('shows Update Payment button for payment_failed status', async () => {
      const failedPaymentRental = createMockRental({
        id: '1',
        status: 'pending',
        displayStatus: 'pending',
        paymentStatus: 'pending',
        paymentFailedNotifiedAt: new Date().toISOString(),
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [failedPaymentRental] });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      expect(screen.getByRole('button', { name: /update payment/i })).toBeInTheDocument();
    });

    it('opens update payment modal on button click', async () => {
      const failedPaymentRental = createMockRental({
        id: '1',
        status: 'pending',
        displayStatus: 'pending',
        paymentStatus: 'pending',
        paymentFailedNotifiedAt: new Date().toISOString(),
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [failedPaymentRental] });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      const updatePaymentButton = screen.getByRole('button', { name: /update payment/i });
      fireEvent.click(updatePaymentButton);

      expect(screen.getByTestId('update-payment-modal')).toBeInTheDocument();
    });
  });

  describe('Condition Checks', () => {
    it('displays condition check buttons when available', async () => {
      const rental = createMockRental({
        id: '1',
        status: 'confirmed',
        displayStatus: 'active',
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [rental] });
      mockedGetAvailableChecks.mockResolvedValue({
        data: { availableChecks: [{ rentalId: '1', checkType: 'rental_start_renter' }] },
      });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /submit rental start condition/i })).toBeInTheDocument();
      });
    });

    it('opens condition check modal', async () => {
      const rental = createMockRental({
        id: '1',
        status: 'confirmed',
        displayStatus: 'active',
        item: mockItemWithOwner as any,
      });
      mockedGetRentals.mockResolvedValue({ data: [rental] });
      mockedGetAvailableChecks.mockResolvedValue({
        data: { availableChecks: [{ rentalId: '1', checkType: 'rental_start_renter' }] },
      });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /submit rental start condition/i })).toBeInTheDocument();
      });

      const checkButton = screen.getByRole('button', { name: /submit rental start condition/i });
      fireEvent.click(checkButton);

      expect(screen.getByTestId('condition-check-modal')).toBeInTheDocument();
    });

    it('shows completed condition checks', async () => {
      const rental = createMockRental({
        id: '1',
        status: 'confirmed',
        displayStatus: 'active',
        item: mockItemWithOwner as any,
      });
      const conditionCheck = createMockConditionCheck({
        rentalId: '1',
        checkType: 'pre_rental_owner',
      });
      mockedGetRentals.mockResolvedValue({ data: [rental] });
      mockedGetBatchConditionChecks.mockResolvedValue({
        data: { conditionChecks: [conditionCheck] },
      });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      await waitFor(() => {
        expect(screen.getByText(/owner's pre-rental condition/i)).toBeInTheDocument();
      });
    });

    it('opens condition check viewer modal', async () => {
      const rental = createMockRental({
        id: '1',
        status: 'confirmed',
        displayStatus: 'active',
        item: mockItemWithOwner as any,
      });
      const conditionCheck = createMockConditionCheck({
        rentalId: '1',
        checkType: 'pre_rental_owner',
      });
      mockedGetRentals.mockResolvedValue({ data: [rental] });
      mockedGetBatchConditionChecks.mockResolvedValue({
        data: { conditionChecks: [conditionCheck] },
      });

      renderRenting();

      await waitFor(() => {
        expect(screen.getByText(/owner's pre-rental condition/i)).toBeInTheDocument();
      });

      const viewButton = screen.getByText(/owner's pre-rental condition/i);
      fireEvent.click(viewButton);

      expect(screen.getByTestId('condition-viewer-modal')).toBeInTheDocument();
    });
  });

  describe('Navigation', () => {
    it('navigates to owner profile on owner name click', async () => {
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      // Find owner name spans (multiple rentals may have owners)
      const ownerElements = screen.getAllByText(/Owner User/);
      fireEvent.click(ownerElements[0]);

      expect(mockNavigate).toHaveBeenCalled();
    });

    it('links to item detail page', async () => {
      renderRenting();

      await waitFor(() => {
        expect(screen.getByText('Test Item')).toBeInTheDocument();
      });

      // The rental card should link to item detail
      const itemLink = screen.getAllByRole('link')[0];
      expect(itemLink).toHaveAttribute('href', expect.stringContaining('/items/'));
    });
  });

  describe('Declined Rentals', () => {
    it('shows decline reason for declined rentals', async () => {
      const declinedRental = createMockRental({
        id: '1',
        status: 'declined',
        displayStatus: 'declined',
        declineReason: 'Item not available during requested dates',
        item: mockItemWithOwner as any,
      });
      // Declined rentals are filtered out from active rentals view
      mockedGetRentals.mockResolvedValue({ data: [declinedRental] });

      renderRenting();

      await waitFor(() => {
        // Since declined rentals are filtered, we should see empty state
        expect(screen.getByText('No Active Rental Requests')).toBeInTheDocument();
      });
    });
  });
});
