import { useMemo } from 'react';
import type { ReactNode } from 'react';
import { Button, Tooltip } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { info } from '@wordpress/icons';
import { hasSubItems, getSubItems } from 'utils';
import {
getDimensionsUnit,
getStoredPackageDimensions,
getWeightUnit,
} from 'utils/config';
import {
ShipmentItem,
ReturnShipmentInfo,
Package,
CustomPackage,
Label,
DimensionUnit,
WeightUnit,
} from 'types';
import { useLabelPurchaseContext } from 'context/label-purchase';
interface LabelWithPackageData extends Label {
packageWeight?: number;
packageWeightUnit?: WeightUnit;
packageLength?: number;
packageWidth?: number;
packageHeight?: number;
packageDimensionsUnit?: DimensionUnit;
packageDimensionsUnitSnapshot?: DimensionUnit;
packageWeightUnitSnapshot?: WeightUnit;
}
interface ReturnLabelButtonProps {
isDomesticShipment: boolean;
shipments: Record< string, ShipmentItem[] >;
currentShipmentId: string;
setShipments: ( shipments: Record< string, ShipmentItem[] > ) => void;
setCurrentShipmentId: ( id: string ) => void;
setReturnShipments: (
callback: (
prev: Record< string, ReturnShipmentInfo >
) => Record< string, ReturnShipmentInfo >
) => void;
returnShipments?: Record< string, ReturnShipmentInfo >;
packages: {
getSelectedPackage: () => Package | CustomPackage | null;
setSelectedPackage: ( pkg: Package | CustomPackage ) => void;
};
selections: Record< string, ShipmentItem[] >;
setSelection: ( selections: Record< string, ShipmentItem[] > ) => void;
className?: string;
children?: ReactNode;
}
export const ReturnLabelButton = ( {
isDomesticShipment,
shipments,
currentShipmentId,
setShipments,
setCurrentShipmentId,
setReturnShipments,
returnShipments,
packages,
selections,
setSelection,
className,
children,
}: ReturnLabelButtonProps ) => {
const { labels } = useLabelPurchaseContext();
// Check if all items have already been returned
const { availableItems, allItemsReturned, currentShipmentItems } =
useMemo( () => {
const shipmentItems = shipments[ currentShipmentId ] || [];
const returnedItemIds = new Set< number | string >();
// Check in-session return shipments (from returnShipments state)
Object.entries( returnShipments ?? {} ).forEach(
( [ shipmentId, returnInfo ] ) => {
if (
returnInfo.parentShipmentId === currentShipmentId &&
returnInfo.isReturn
) {
// Use selections to get the actual items in this return shipment
const returnShipmentItems =
selections[ shipmentId ] || [];
returnShipmentItems.forEach( ( item ) => {
if ( item?.id ) {
returnedItemIds.add( item.id );
}
} );
}
}
);
// Also check ALL purchased labels to handle page reloads
// Look for return labels that have parent_shipment_id matching current shipment
Object.keys( shipments ).forEach( ( shipmentId ) => {
// Skip the current shipment itself
if ( shipmentId === currentShipmentId ) {
return;
}
const shipmentLabel =
labels.getCurrentShipmentLabel( shipmentId );
// Check if this is a return label for the current shipment
if (
shipmentLabel &&
shipmentLabel.isReturn &&
shipmentLabel.parentShipmentId === currentShipmentId
) {
// This is a return label for the current shipment
// Mark all items from this shipment as returned
const returnShipmentItems = shipments[ shipmentId ] || [];
returnShipmentItems.forEach( ( item ) => {
if ( item?.id ) {
returnedItemIds.add( item.id );
}
} );
}
} );
const itemsAvailableForReturn = shipmentItems.filter(
( orderItem ) => {
if ( orderItem === null ) {
return false;
}
// Check if this specific order item has already been returned
return ! returnedItemIds.has( orderItem.id );
}
);
return {
availableItems: itemsAvailableForReturn,
allItemsReturned: itemsAvailableForReturn.length === 0,
currentShipmentItems: shipmentItems,
};
}, [
shipments,
returnShipments,
selections,
currentShipmentId,
labels,
] );
const createReturnLabel = () => {
try {
const newShipmentId = Object.keys( shipments ).length;
const newShipmentIdString = `${ newShipmentId }`;
// This check should never be reached since button is disabled, but keep as safety
if ( allItemsReturned ) {
return;
}
// Capture parent shipment ID BEFORE changing currentShipmentId
// If current shipment is already a return, use its parent; otherwise use current
const parentShipmentId =
returnShipments?.[ currentShipmentId ]?.parentShipmentId ??
currentShipmentId;
const newShipment = availableItems.map( ( orderItem ) => ( {
...orderItem,
subItems: orderItem.subItems || [],
} ) );
const allItems = newShipment
.map( ( item ) =>
hasSubItems( item ) ? getSubItems( item ) : item
)
.flat();
setShipments( {
...shipments,
[ newShipmentIdString ]: newShipment,
} );
setCurrentShipmentId( newShipmentIdString );
try {
// Get package details from the purchased label for the current shipment
let originalPackageDetails = null;
const currentLabel =
labels.getCurrentShipmentLabel( currentShipmentId );
// Try to get package dimensions from stored metadata first
// This is more reliable for custom packages
const storedPackageDimensions = getStoredPackageDimensions();
if (
storedPackageDimensions &&
typeof storedPackageDimensions === 'object'
) {
const shipmentKey =
`shipment_${ currentShipmentId }` as keyof typeof storedPackageDimensions;
const shipmentDimensions =
storedPackageDimensions[ shipmentKey ];
if ( shipmentDimensions?.[ 0 ] ) {
originalPackageDetails = shipmentDimensions[ 0 ];
}
}
// Fallback: try to get from label object (for backward compatibility)
if ( ! originalPackageDetails && currentLabel ) {
const labelWithPackageData =
currentLabel as LabelWithPackageData;
if (
labelWithPackageData.packageWeight !== undefined ||
labelWithPackageData.packageLength !== undefined ||
labelWithPackageData.packageWidth !== undefined ||
labelWithPackageData.packageHeight !== undefined
) {
originalPackageDetails = {
package_weight: labelWithPackageData.packageWeight,
package_weight_unit:
labelWithPackageData.packageWeightUnit ??
labelWithPackageData.packageWeightUnitSnapshot ??
getWeightUnit(),
package_length: labelWithPackageData.packageLength,
package_width: labelWithPackageData.packageWidth,
package_height: labelWithPackageData.packageHeight,
package_dimensions_unit:
labelWithPackageData.packageDimensionsUnit ??
labelWithPackageData.packageDimensionsUnitSnapshot ??
getDimensionsUnit(),
};
}
}
// Determine preservedPackage based on available data
let preservedPackage;
if ( currentLabel && originalPackageDetails ) {
// Preserve exact package data from stored dimensions or label
preservedPackage = {
packageWeight: originalPackageDetails.package_weight,
packageWeightUnit:
originalPackageDetails.package_weight_unit ??
originalPackageDetails.package_weight_unit_snapshot ??
getWeightUnit(),
packageLength: originalPackageDetails.package_length,
packageWidth: originalPackageDetails.package_width,
packageHeight: originalPackageDetails.package_height,
packageDimensionsUnit:
originalPackageDetails.package_dimensions_unit ??
originalPackageDetails.package_dimensions_unit_snapshot ??
getDimensionsUnit(),
packageName: currentLabel.packageName,
packageId: currentLabel.id,
};
} else if ( currentLabel ) {
// Label exists but no dimensions - use label data only
preservedPackage = {
packageName: currentLabel.packageName,
packageId: currentLabel.id,
};
} else {
// Fallback: calculate weight from items if no parent label
preservedPackage = {
totalWeight: currentShipmentItems.reduce(
( total, item ) => {
const weight = parseFloat( item.weight || '0' );
const quantity = item.quantity || 1;
return total + weight * quantity;
},
0
),
};
}
setReturnShipments( ( prev ) => ( {
...prev,
[ newShipmentIdString ]: {
isReturn: true,
parentShipmentId,
preservedPackage,
originalPackageDetails:
originalPackageDetails ?? undefined,
},
} ) );
} catch {
// Handle setReturnShipments errors gracefully
}
setSelection( {
...selections,
[ newShipmentIdString ]: allItems,
} );
try {
const selectedPackage = packages.getSelectedPackage();
if ( selectedPackage ) {
packages.setSelectedPackage( selectedPackage );
}
} catch {
// Handle package errors gracefully - continue without package selection
}
} catch {
// Handle any other errors gracefully
}
};
// Determine tooltip text and disabled state
let tooltipText = '';
let isDisabled = false;
if ( ! isDomesticShipment ) {
tooltipText = __(
'Return labels are only available for domestic shipments (US to US)',
'woocommerce-shipping'
);
isDisabled = true;
} else if ( allItemsReturned ) {
tooltipText = __(
'All items from this shipment have already been returned',
'woocommerce-shipping'
);
isDisabled = true;
}
if ( isDisabled ) {
return (
);
}
return (
);
};