/**
 * Return Shipment Test Utilities
 *
 * Comprehensive test utilities for testing return label functionality,
 * including mock setups, error scenarios, and integration test helpers.
 */

import { useState } from 'react';
import { renderHook } from '@testing-library/react';
// eslint-disable-next-line import/named
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
import { SHIPMENT_TYPE } from 'utils/shipment';
import { LABEL_PURCHASE_STATUS } from 'data/constants';
import { address, destinationAddress } from './test-utils';

export interface ReturnShipmentTestConfig {
	shipmentId?: string;
	isReturn?: boolean;
	hasExistingShipments?: boolean;
	isDomestic?: boolean;
	hasValidAddresses?: boolean;
}

export interface ErrorScenario {
	type: 'api_error' | 'network_error' | 'validation_error' | 'address_error';
	message: string;
	statusCode?: number;
}

/**
 * Creates comprehensive mock setup for return shipment testing
 */
export const createReturnShipmentMocks = (
	config: ReturnShipmentTestConfig = {}
) => {
	const {
		shipmentId = '1',
		isReturn = true,
		hasExistingShipments = true,
		isDomestic = true,
		hasValidAddresses = true,
	} = config;

	const mockSelectedRates = hasExistingShipments
		? {
				'0': { rate: { isReturn: false } },
				[ shipmentId ]: { rate: { isReturn } },
		  }
		: {
				[ shipmentId ]: { rate: { isReturn } },
		  };

	const mockOriginAddress = {
		...address,
		country: isDomestic ? 'US' : 'CA',
	};

	const mockDestinationAddress = {
		...destinationAddress,
		country: isDomestic ? 'US' : 'CA',
		isVerified: hasValidAddresses,
	};

	return {
		wordPressMocks: {
			useSelect: jest.fn().mockImplementation( ( selector ) =>
				selector( () => ( {
					getSelectedRates: jest
						.fn()
						.mockReturnValue( mockSelectedRates ),
					getPurchasedLabel: jest.fn().mockReturnValue( null ),
					getOriginAddresses: jest
						.fn()
						.mockReturnValue( [ mockOriginAddress ] ),
					getLabelDestinations: jest
						.fn()
						.mockReturnValue( [ mockDestinationAddress ] ),
					getCurrentOrderShipments: jest
						.fn()
						.mockReturnValue(
							hasExistingShipments
								? { 0: [], [ shipmentId ]: [] }
								: {}
						),
					getRefundedLabel: jest.fn().mockReturnValue( null ),
					getOrderStatus: jest.fn().mockReturnValue( 'processing' ),
				} ) )
			),
			select: jest.fn().mockReturnValue( {
				getPurchasedLabel: jest.fn().mockReturnValue( null ),
				getSelectedRates: jest
					.fn()
					.mockReturnValue( mockSelectedRates ),
				getOriginAddresses: jest
					.fn()
					.mockReturnValue( [ mockOriginAddress ] ),
			} ),
			dispatch: jest.fn().mockReturnValue( {
				updateShipments: jest.fn(),
				purchaseLabel: jest.fn(),
				refundLabel: jest.fn(),
			} ),
		},
		shipmentMocks: {
			getShipmentOrigin: jest.fn().mockReturnValue( mockOriginAddress ),
			getShipmentPurchaseOrigin: jest
				.fn()
				.mockReturnValue( mockOriginAddress ),
			getShipmentDestination: jest
				.fn()
				.mockReturnValue( mockDestinationAddress ),
			getCurrentShipmentIsReturn: jest.fn().mockReturnValue( isReturn ),
			isReturnShipment: jest
				.fn()
				.mockImplementation( ( id ) => id === shipmentId && isReturn ),
			getShipmentType: jest
				.fn()
				.mockImplementation( ( id ) =>
					id === shipmentId && isReturn
						? SHIPMENT_TYPE.RETURN
						: SHIPMENT_TYPE.OUTBOUND
				),
			getCurrentShipmentDate: jest.fn().mockReturnValue( {
				shippingDate: isReturn ? null : new Date( '2025-02-26' ),
				estimatedDeliveryDate: new Date( '2025-02-28' ),
			} ),
			returnShipments: isReturn
				? { [ shipmentId ]: { isReturn: true } }
				: {},
			setReturnShipments: jest.fn(),
		},
		contextMocks: {
			storeCurrency: {
				formatMoney: jest.fn( ( amount ) => `$${ amount }` ),
			},
			rates: {
				getSelectedRate: jest.fn().mockReturnValue( null ),
				getSelectedRateOptions: jest.fn().mockReturnValue( [] ),
				selectRateOption: jest.fn(),
				getCurrentRateOptions: jest.fn().mockReturnValue( [] ),
				isFetching: false,
				errors: [],
			},
			labels: {
				hasPurchasedLabel: jest.fn().mockReturnValue( false ),
				getCurrentShipmentLabel: jest.fn().mockReturnValue( {
					isLegacy: false,
					status: LABEL_PURCHASE_STATUS.PURCHASED,
				} ),
				isPurchasing: false,
				errors: [],
			},
		},
		apiMocks: {
			getRates: jest.fn(),
			purchaseLabel: jest.fn(),
			refundLabel: jest.fn(),
		},
		addresses: {
			origin: mockOriginAddress,
			destination: mockDestinationAddress,
		},
	};
};

/**
 * Setup mock error scenarios for return shipment testing
 */
export const setupErrorScenarios = {
	apiError: ( scenario: ErrorScenario ) => {
		const mockError = new Error( scenario.message ) as Error & {
			status?: number;
		};
		if ( scenario.statusCode ) {
			mockError.status = scenario.statusCode;
		}

		switch ( scenario.type ) {
			case 'api_error':
				global.fetch = jest.fn().mockRejectedValue( mockError );
				break;
			case 'network_error':
				global.fetch = jest
					.fn()
					.mockRejectedValue( new Error( 'Network Error' ) );
				break;
			case 'validation_error':
				return {
					validation: {
						isValid: false,
						errors: [ scenario.message ],
					},
				};
			case 'address_error':
				return {
					address: {
						isVerified: false,
						errors: [ scenario.message ],
					},
				};
		}
		return mockError;
	},

	clearApiMocks: () => {
		const fetchMock = global.fetch as
			| jest.MockedFunction< typeof fetch >
			| undefined;
		if ( fetchMock && typeof fetchMock.mockRestore === 'function' ) {
			fetchMock.mockRestore();
		}
	},
};

/**
 * Enhanced test utilities for return shipment integration testing
 */
export const returnShipmentIntegrationUtils = {
	/**
	 * Setup complete return label purchase flow test
	 */
	setupReturnLabelPurchaseFlow: async (
		config: ReturnShipmentTestConfig = {}
	) => {
		const mocks = createReturnShipmentMocks( config );

		// Mock successful API responses
		mocks.apiMocks.getRates.mockResolvedValue( {
			rates: [
				{
					id: 'rate_1',
					service: 'USPS Ground',
					rate: 5.99,
					isReturn: config.isReturn ?? true,
				},
			],
		} );

		mocks.apiMocks.purchaseLabel.mockResolvedValue( {
			label: {
				id: 'label_1',
				status: LABEL_PURCHASE_STATUS.PURCHASED,
				tracking_number: 'TRACK123',
				label_url: 'https://example.com/label.pdf',
			},
		} );

		return mocks;
	},

	/**
	 * Test return label creation with address swapping
	 */
	testAddressSwapping: async ( component: JSX.Element ) => {
		render( component );

		// For return shipments, "Ship from" should show destination address
		// and "Return to" should show origin address
		await waitFor( () => {
			expect( screen.getByText( 'Ship from' ) ).toBeInTheDocument();
			expect( screen.getByText( 'Return to' ) ).toBeInTheDocument();
		} );

		// Verify address order is swapped for returns
		const shipFromSection = screen
			.getByText( 'Ship from' )
			.closest( '.components-base-control' );
		const returnToSection = screen
			.getByText( 'Return to' )
			.closest( '.components-base-control' );

		expect( shipFromSection ).toBeInTheDocument();
		expect( returnToSection ).toBeInTheDocument();

		// Ship from should appear before Return to in the DOM for returns
		const sections = [ shipFromSection, returnToSection ];
		expect( sections[ 0 ] ).toBe( shipFromSection );
		expect( sections[ 1 ] ).toBe( returnToSection );
	},

	/**
	 * Test domestic-only restriction for return labels
	 */
	testDomesticRestriction: async ( component: JSX.Element ) => {
		render( component );

		const returnButton = await screen.findByRole( 'button', {
			name: /return label/i,
		} );

		// International orders should have disabled return button
		expect( returnButton ).toBeDisabled();

		// Should show tooltip explaining restriction
		fireEvent.mouseOver( returnButton );

		await waitFor( () => {
			expect(
				screen.getByText(
					/return labels are currently only available for domestic shipments/i
				)
			).toBeInTheDocument();
		} );
	},

	/**
	 * Test return shipment validation
	 */
	validateReturnShipment: ( shipmentData: {
		isReturn?: boolean;
		origin?: { country?: string };
		destination?: { country?: string };
		items?: unknown[];
	} ) => {
		const validationErrors: string[] = [];

		if ( ! shipmentData.isReturn ) {
			validationErrors.push( 'Shipment must be marked as return' );
		}

		if ( ! shipmentData.origin || ! shipmentData.destination ) {
			validationErrors.push(
				'Both origin and destination addresses are required'
			);
		}

		if (
			shipmentData.origin?.country !== shipmentData.destination?.country
		) {
			validationErrors.push(
				'Return labels only support domestic shipments'
			);
		}

		if ( ! shipmentData.items || shipmentData.items.length === 0 ) {
			validationErrors.push(
				'At least one item must be selected for return'
			);
		}

		return {
			isValid: validationErrors.length === 0,
			errors: validationErrors,
		};
	},

	/**
	 * Mock successful return label purchase
	 */
	mockSuccessfulReturnPurchase: ( config: ReturnShipmentTestConfig = {} ) => {
		const mocks = createReturnShipmentMocks( config );

		mocks.apiMocks.purchaseLabel.mockResolvedValue( {
			success: true,
			data: {
				label: {
					id: `return_label_${ config.shipmentId ?? '1' }`,
					status: LABEL_PURCHASE_STATUS.PURCHASED,
					tracking_number: `RET${ Date.now() }`,
					label_url: 'https://example.com/return-label.pdf',
					isReturn: true,
				},
			},
		} );

		return mocks;
	},

	/**
	 * Mock failed return label purchase
	 */
	mockFailedReturnPurchase: (
		errorType: ErrorScenario[ 'type' ] = 'api_error'
	) => {
		const mocks = createReturnShipmentMocks( { isReturn: true } );

		const errorMessage = {
			api_error: 'API service unavailable',
			network_error: 'Network connection failed',
			validation_error: 'Invalid return shipment data',
			address_error: 'Return address validation failed',
		}[ errorType ];

		mocks.apiMocks.purchaseLabel.mockRejectedValue(
			new Error( errorMessage )
		);

		return mocks;
	},
};

/**
 * Focused assertion utilities instead of large snapshots
 */
export const returnShipmentAssertions = {
	/**
	 * Assert return label UI elements are present and correct
	 */
	assertReturnLabelUI: async () => {
		await waitFor( () => {
			expect(
				screen.getByRole( 'button', { name: /return label/i } )
			).toBeInTheDocument();
		} );
	},

	/**
	 * Assert address fields are in correct order for return shipments
	 */
	assertReturnAddressOrder: async () => {
		await waitFor( () => {
			const shipFromLabel = screen.getByText( 'Ship from' );
			const returnToLabel = screen.getByText( 'Return to' );

			expect( shipFromLabel ).toBeInTheDocument();
			expect( returnToLabel ).toBeInTheDocument();

			// Verify proper labeling
			expect( shipFromLabel ).toHaveAttribute( 'for', 'ship-to' ); // Customer address for returns
			expect( returnToLabel ).toHaveAttribute( 'for', 'ship-from' ); // Merchant address for returns
		} );
	},

	/**
	 * Assert return shipment doesn't show shipping date field
	 */
	assertNoShippingDateForReturns: () => {
		expect(
			screen.queryByLabelText( /ship date/i )
		).not.toBeInTheDocument();
	},

	/**
	 * Assert return label button state and behavior
	 */
	assertReturnButtonBehavior: async ( isDomestic = true ) => {
		const returnButton = screen.getByRole( 'button', {
			name: /return label/i,
		} );

		if ( isDomestic ) {
			expect( returnButton ).toBeEnabled();
		} else {
			expect( returnButton ).toBeDisabled();

			// Check for international restriction tooltip
			fireEvent.mouseOver( returnButton );

			await waitFor( () => {
				expect(
					screen.getByText( /domestic shipments/i )
				).toBeInTheDocument();
			} );
		}
	},

	/**
	 * Assert return shipment tab labeling
	 */
	assertReturnShipmentTabs: async ( totalShipments = 2 ) => {
		// Wait for tabs to render
		await waitFor( () => {
			// Should see "Return X/Y" format for return tabs
			expect(
				screen.getByText( `Return 1/${ totalShipments }` )
			).toBeInTheDocument();
		} );
	},

	/**
	 * Assert purchase button text for returns
	 */
	assertReturnPurchaseButton: async ( returnIndex = 1, totalReturns = 1 ) => {
		await waitFor( () => {
			const purchaseButton = screen.getByRole( 'button', {
				name: new RegExp(
					`return ${ returnIndex }/${ totalReturns }`,
					'i'
				),
			} );
			expect( purchaseButton ).toBeInTheDocument();
		} );
	},

	/**
	 * Assert error handling for return shipments
	 */
	assertReturnErrorHandling: async ( expectedError: string ) => {
		await waitFor( () => {
			expect( screen.getByText( expectedError ) ).toBeInTheDocument();
		} );
	},
};

/**
 * Hook testing utilities for return shipments
 */
export const returnShipmentHookUtils = {
	/**
	 * Test return shipment state management
	 */
	testReturnShipmentState: () => {
		const { result } = renderHook( () => {
			// Mock implementation for testing
			const [ returnShipments, setReturnShipments ] = useState<
				Record< string, { isReturn?: boolean } >
			>( {} );

			return {
				returnShipments,
				setReturnShipments,
				isReturnShipment: ( id: string ) =>
					returnShipments[ id ]?.isReturn ?? false,
				getCurrentShipmentIsReturn: () =>
					returnShipments[ '1' ]?.isReturn ?? false,
				getShipmentType: ( id: string ) =>
					returnShipments[ id ]?.isReturn
						? SHIPMENT_TYPE.RETURN
						: SHIPMENT_TYPE.OUTBOUND,
			};
		} );

		return result;
	},

	/**
	 * Test return shipment validation hooks
	 */
	testReturnShipmentValidation: ( shipmentData: {
		isReturn?: boolean;
		origin?: { country?: string };
		destination?: { country?: string };
		items?: unknown[];
	} ) => {
		const { result } = renderHook( () => {
			const validate = () =>
				returnShipmentIntegrationUtils.validateReturnShipment(
					shipmentData
				);
			return { validate };
		} );

		return result;
	},
};

/**
 * Common test setup for return shipment components
 * Note: This should be called in the main describe block, not inside individual tests
 */
export const setupReturnShipmentTest = (
	config: ReturnShipmentTestConfig = {}
) => {
	const mocks = createReturnShipmentMocks( config );
	return mocks;
};
