/**
 * Owning Page Tests
 *
 * Tests for the owner dashboard page showing listings,
 * rental 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 Owning from '../../pages/Owning';
import { useAuth } from '../../contexts/AuthContext';
import api, { rentalAPI, conditionCheckAPI } from '../../services/api';
import {
  mockUser,
  mockItemWithOwner,
  createMockRental,
  createMockItem,
  createMockConditionCheck,
} from '../../mocks/handlers';

// Mock dependencies
vi.mock('../../contexts/AuthContext');
vi.mock('../../services/api', () => {
  const defaultApi = {
    get: vi.fn(),
    put: vi.fn(),
    delete: vi.fn(),
  };
  return {
    default: defaultApi,
    rentalAPI: {
      getListings: vi.fn(),
      updateRentalStatus: 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/ReviewRenterModal', () => ({
  default: ({ show, onClose, rental }: any) =>
    show ? (
      <div data-testid="review-renter-modal">
        <span>Review Renter for {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/DeclineRentalModal', () => ({
  default: ({ show, onHide, rental, onDeclineComplete }: any) =>
    show ? (
      <div data-testid="decline-modal">
        <span>Decline {rental?.item?.name}</span>
        <button onClick={onHide}>Close</button>
        <button onClick={() => onDeclineComplete({ ...rental, status: 'declined' })}>
          Confirm Decline
        </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/ReturnStatusModal', () => ({
  default: ({ show, onHide, rental, onReturnMarked }: any) =>
    show ? (
      <div data-testid="return-status-modal">
        <span>Return Status for {rental?.item?.name}</span>
        <button onClick={onHide}>Close</button>
        <button onClick={() => onReturnMarked({ ...rental, status: 'completed' })}>
          Mark Returned
        </button>
      </div>
    ) : null,
}));

vi.mock('../../components/PaymentFailedModal', () => ({
  default: ({ show, onHide, paymentError, itemName }: any) =>
    show ? (
      <div data-testid="payment-failed-modal">
        <span>Payment Failed for {itemName}</span>
        <span>{paymentError?.message}</span>
        <button onClick={onHide}>Close</button>
      </div>
    ) : null,
}));

vi.mock('../../components/AuthenticationRequiredModal', () => ({
  default: ({ rental, onClose }: any) => (
    <div data-testid="auth-required-modal">
      <span>Auth Required for {rental?.item?.name}</span>
      <button onClick={onClose}>Close</button>
    </div>
  ),
}));

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 mockedApiGet = api.get as MockedFunction<typeof api.get>;
const mockedApiPut = api.put as MockedFunction<typeof api.put>;
const mockedApiDelete = api.delete as MockedFunction<typeof api.delete>;
const mockedGetListings = rentalAPI.getListings as MockedFunction<typeof rentalAPI.getListings>;
const mockedUpdateRentalStatus = rentalAPI.updateRentalStatus as MockedFunction<typeof rentalAPI.updateRentalStatus>;
const mockedGetAvailableChecks = conditionCheckAPI.getAvailableChecks as MockedFunction<typeof conditionCheckAPI.getAvailableChecks>;
const mockedGetBatchConditionChecks = conditionCheckAPI.getBatchConditionChecks as MockedFunction<typeof conditionCheckAPI.getBatchConditionChecks>;

// Helper to render Owning page
const renderOwning = (authOverrides: Partial<ReturnType<typeof useAuth>> = {}) => {
  const user = { ...mockUser, id: '2' }; // Same as mockItemWithOwner.ownerId
  mockedUseAuth.mockReturnValue({
    user: user as any,
    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={['/owning']}>
      <Routes>
        <Route path="/owning" element={<Owning />} />
      </Routes>
    </MemoryRouter>
  );
};

describe('Owning', () => {
  const mockListings = [
    createMockItem({ id: '1', name: 'Camping Tent', ownerId: '2', isAvailable: true }),
    createMockItem({ id: '2', name: 'Mountain Bike', ownerId: '2', isAvailable: false }),
  ];

  const mockOwnerRentals = [
    createMockRental({
      id: '1',
      status: 'pending',
      displayStatus: 'pending',
      item: mockItemWithOwner as any,
      renter: mockUser as any,
    }),
    createMockRental({
      id: '2',
      status: 'confirmed',
      displayStatus: 'active',
      item: { ...mockItemWithOwner, name: 'Mountain Bike' } as any,
      renter: mockUser as any,
    }),
  ];

  beforeEach(() => {
    vi.clearAllMocks();
    mockedApiGet.mockResolvedValue({ data: { items: mockListings } });
    mockedGetListings.mockResolvedValue({ data: mockOwnerRentals });
    mockedGetAvailableChecks.mockResolvedValue({ data: { availableChecks: [] } });
    mockedGetBatchConditionChecks.mockResolvedValue({ data: { conditionChecks: [] } });
    mockedUpdateRentalStatus.mockResolvedValue({ data: { paymentStatus: 'paid' } });

    // Mock window.dispatchEvent
    vi.spyOn(window, 'dispatchEvent').mockImplementation(() => true);
  });

  describe('Loading State', () => {
    it('shows loading spinner while fetching data', () => {
      mockedApiGet.mockImplementation(() => new Promise(() => {}));
      renderOwning();

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

  describe('Listings Display', () => {
    it('fetches and displays user listings', async () => {
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      // Mountain Bike appears in both the Rentals section (as an active rental)
      // and the Listings section, so we use getAllByText
      await waitFor(() => {
        expect(screen.getAllByText('Mountain Bike').length).toBeGreaterThanOrEqual(1);
      });
    });

    it('shows availability status badges', async () => {
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      expect(screen.getByText('Available')).toBeInTheDocument();
      expect(screen.getByText('Not Available')).toBeInTheDocument();
    });

    it('has edit button for each listing', async () => {
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      const editLinks = screen.getAllByRole('link', { name: /edit/i });
      expect(editLinks.length).toBeGreaterThanOrEqual(2);
    });

    it('has add new item link', async () => {
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      expect(screen.getByRole('link', { name: /add new item/i })).toHaveAttribute('href', '/create-item');
    });
  });

  describe('Empty Listings State', () => {
    it('shows empty state when no listings', async () => {
      mockedApiGet.mockResolvedValue({ data: { items: [] } });
      mockedGetListings.mockResolvedValue({ data: [] });

      renderOwning();

      await waitFor(() => {
        expect(screen.getByText(/haven't listed any items yet/i)).toBeInTheDocument();
      });

      expect(screen.getByRole('link', { name: /list your first item/i })).toBeInTheDocument();
    });
  });

  describe('Toggle Availability', () => {
    it('toggles item availability', async () => {
      mockedApiPut.mockResolvedValue({ data: {} });
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      const toggleButtons = screen.getAllByRole('button', { name: /mark (un)?available/i });
      fireEvent.click(toggleButtons[0]);

      await waitFor(() => {
        expect(mockedApiPut).toHaveBeenCalledWith(
          '/items/1',
          expect.objectContaining({ isAvailable: false })
        );
      });
    });
  });

  describe('Delete Item', () => {
    it('shows delete button for each listing', async () => {
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByRole('button', { name: /delete/i });
      expect(deleteButtons.length).toBeGreaterThanOrEqual(2);
    });

    it('opens delete confirmation modal', async () => {
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

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

      expect(screen.getByText('Delete Listing')).toBeInTheDocument();
      expect(screen.getByText(/are you sure you want to delete/i)).toBeInTheDocument();
    });

    it('deletes item on confirmation', async () => {
      mockedApiDelete.mockResolvedValue({ data: {} });
      renderOwning();

      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

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

      // Find and click confirm button in modal
      const confirmButtons = screen.getAllByRole('button', { name: /delete/i });
      const modalDeleteButton = confirmButtons[confirmButtons.length - 1];
      fireEvent.click(modalDeleteButton);

      await waitFor(() => {
        expect(mockedApiDelete).toHaveBeenCalledWith('/items/1');
      });
    });
  });

  describe('Rental Requests', () => {
    it('displays rental requests', async () => {
      renderOwning();

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

      expect(screen.getByText('Test Item')).toBeInTheDocument();
    });

    it('shows renter information', async () => {
      renderOwning();

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

      // Find renter names
      expect(screen.getAllByText(/Test User/i).length).toBeGreaterThan(0);
    });

    it('shows accept and decline buttons for pending requests', async () => {
      renderOwning();

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

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

  describe('Accept Rental', () => {
    it('accepts rental and triggers payment', async () => {
      renderOwning();

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

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

      await waitFor(() => {
        expect(mockedUpdateRentalStatus).toHaveBeenCalledWith('1', 'confirmed');
      });
    });

    it('shows processing state during accept', async () => {
      mockedUpdateRentalStatus.mockImplementation(() => new Promise(() => {}));
      renderOwning();

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

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

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

    it('shows payment failed modal on payment error', async () => {
      mockedUpdateRentalStatus.mockRejectedValue({
        response: { status: 402, data: { error: 'payment_failed', message: 'Card declined' } },
      });

      renderOwning();

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

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

      await waitFor(() => {
        expect(screen.getByTestId('payment-failed-modal')).toBeInTheDocument();
      });
    });

    it('shows auth required modal on 3DS requirement', async () => {
      mockedUpdateRentalStatus.mockRejectedValue({
        response: { data: { error: 'authentication_required' } },
      });

      renderOwning();

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

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

      await waitFor(() => {
        expect(screen.getByTestId('auth-required-modal')).toBeInTheDocument();
      });
    });
  });

  describe('Decline Rental', () => {
    it('opens decline modal', async () => {
      renderOwning();

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

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

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

  describe('Cancel Rental', () => {
    it('shows cancel button for confirmed rentals', async () => {
      const confirmedRental = createMockRental({
        id: '3',
        status: 'confirmed',
        displayStatus: 'confirmed',
        item: mockItemWithOwner as any,
        renter: mockUser as any,
      });
      mockedGetListings.mockResolvedValue({ data: [confirmedRental] });

      renderOwning();

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

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

    it('opens cancellation modal', async () => {
      const confirmedRental = createMockRental({
        id: '3',
        status: 'confirmed',
        displayStatus: 'confirmed',
        item: mockItemWithOwner as any,
        renter: mockUser as any,
      });
      mockedGetListings.mockResolvedValue({ data: [confirmedRental] });

      renderOwning();

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

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

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

  describe('Complete Rental', () => {
    it('shows complete button for active rentals', async () => {
      renderOwning();

      // Wait for both listings and rentals to load
      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      // Wait for the Rentals section to be displayed with the Mountain Bike rental
      await waitFor(() => {
        expect(screen.getByText('Rentals')).toBeInTheDocument();
      });

      // The active rental with Mountain Bike should have a Complete button
      await waitFor(() => {
        expect(screen.getByRole('button', { name: /complete/i })).toBeInTheDocument();
      });
    });

    it('opens return status modal', async () => {
      renderOwning();

      // Wait for both listings and rentals to load
      await waitFor(() => {
        expect(screen.getByText('Camping Tent')).toBeInTheDocument();
      });

      // Wait for the Rentals section to be displayed
      await waitFor(() => {
        expect(screen.getByText('Rentals')).toBeInTheDocument();
      });

      // Wait for the Complete button to appear
      await waitFor(() => {
        expect(screen.getByRole('button', { name: /complete/i })).toBeInTheDocument();
      });

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

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

  describe('Condition Checks', () => {
    it('displays pre-rental condition check button', async () => {
      mockedGetAvailableChecks.mockResolvedValue({
        data: { availableChecks: [{ rentalId: '1', checkType: 'pre_rental_owner' }] },
      });

      renderOwning();

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

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

    it('shows completed condition checks', async () => {
      const conditionCheck = createMockConditionCheck({
        rentalId: '1',
        checkType: 'pre_rental_owner',
      });
      mockedGetBatchConditionChecks.mockResolvedValue({
        data: { conditionChecks: [conditionCheck] },
      });

      renderOwning();

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

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

  describe('Error Handling', () => {
    it('displays error on listing fetch failure', async () => {
      mockedApiGet.mockRejectedValue({
        response: { status: 500, data: { message: 'Server error' } },
      });

      renderOwning();

      await waitFor(() => {
        expect(screen.getByRole('alert')).toBeInTheDocument();
        expect(screen.getByText(/failed to get your listings/i)).toBeInTheDocument();
      });
    });
  });
});
