/**
 * Data Preview — table with formatted data, count badge, export options.
 *
 * @package RawTypeOE
 */

import { useCallback, useEffect, useMemo, useRef, useState } from '@wordpress/element';
import { __, _n, sprintf } from '@wordpress/i18n';
import { applyFilters } from '@wordpress/hooks';
import {
	Badge,
	Button,
	Card,
	CardContent,
	CardDescription,
	CardFooter,
	CardHeader,
	CardTitle,
	Checkbox,
	EmptyState,
	Notice,
	RadioGroup,
	Select,
	Separator,
	Skeleton,
	SkeletonGroup,
	Table,
	Toast,
} from '@rawtype/ui';
import apiFetch from '../utils/apiFetch';
import { MULTISHEET_CONFIG } from '../constants/entities';
const MODE_LABELS = {
	single: __( 'Data in one sheet', 'rawtype-order-exporter-woocommerce' ),
	by_entity: __( 'Sheet per entity', 'rawtype-order-exporter-woocommerce' ),
	by_group: __( 'Sheet per category/status', 'rawtype-order-exporter-woocommerce' ),
};

// Client-side ceiling for a synchronous export request (5 minutes).
const EXPORT_TIMEOUT_MS = 5 * 60 * 1000;

/**
 * Decode HTML entities (e.g. &nbsp;&euro;) into their readable characters.
 */
const _decodeTextarea = document.createElement( 'textarea' );
function decodeHtmlEntities( str ) {
	if ( 'string' !== typeof str ) {
		return str;
	}
	_decodeTextarea.innerHTML = str;
	return _decodeTextarea.value;
}

function PreviewSkeleton( { columnCount } ) {
	const cols = columnCount || 5;
	return (
		<SkeletonGroup>
			<Skeleton width={ 160 } height={ 24 } />
			{ Array.from( { length: 5 } ).map( ( _, i ) => (
				<Skeleton key={ i } width="100%" height={ 36 } />
			) ) }
		</SkeletonGroup>
	);
}

export default function DataPreview( {
	entity,
	filters,
	logic,
	selectedColumns,
	getColumnsForExport,
	previewCount,
	exportFormat,
	onSetExportFormat,
	exportOptions,
	onUpdateExportOptions,
	crossEntityConfig,
	crossEntityValid,
	calculatedFields,
	onPrev,
	onReset,
	onSaveProfile,
	visible = true,
} ) {
	const [ previewData, setPreviewData ] = useState( null );
	const [ loading, setLoading ] = useState( false );
	const [ error, setError ] = useState( null );
	const [ toast, setToast ] = useState( { open: false, type: 'info', message: '' } );
	const multisheetConfig = MULTISHEET_CONFIG[ entity ] || null;
	const availableModes = multisheetConfig ? multisheetConfig.modes : [];
	const availableGroupFields = multisheetConfig
		? applyFilters( 'rawtype_oe_multisheet_group_fields', multisheetConfig.groupFields, entity )
		: [];

	const multisheetMode = exportOptions?.multisheet_mode || 'single';
	const multisheetGroupField = exportOptions?.group_field || multisheetConfig?.groupFields?.[ 0 ]?.value || 'status';

	const previewAbortRef = useRef( null );

	// Only these options affect the preview data; multisheet_mode / group_field
	// are export-only and must NOT trigger a re-fetch.
	const itemMode = exportOptions?.item_mode;
	const includeGuests = exportOptions?.include_guests;
	const exportOptionsRef = useRef( exportOptions );
	exportOptionsRef.current = exportOptions;

	const loadPreview = useCallback( () => {
		if ( ! entity || 0 === selectedColumns.length ) {
			return;
		}

		if ( previewAbortRef.current ) {
			previewAbortRef.current.abort();
		}
		const controller = new AbortController();
		previewAbortRef.current = controller;

		setLoading( true );
		setError( null );

		const columns = getColumnsForExport;
		const currentOptions = exportOptionsRef.current;
		const payload = {
			entity,
			filters,
			logic,
			columns,
			calculated_fields: calculatedFields,
			limit: 10,
			count_only: false,
		};

		if ( currentOptions && Object.keys( currentOptions ).length > 0 ) {
			payload.options = currentOptions;
		}

		if ( 'cross-entity' === entity ) {
			payload.cross_entity = crossEntityConfig || {};
		}

		apiFetch( {
			path: '/rawtype_oe/v1/preview',
			method: 'POST',
			data: payload,
			signal: controller.signal,
		} )
			.then( ( data ) => {
				setPreviewData( data );
				setLoading( false );
			} )
			.catch( ( err ) => {
				if ( 'AbortError' !== err.name ) {
					setError( err );
					setLoading( false );
				}
			} );
	}, [ entity, filters, logic, selectedColumns, getColumnsForExport, crossEntityConfig, calculatedFields, itemMode, includeGuests ] );

	useEffect( () => {
		if ( visible ) {
			loadPreview();
		}
		return () => {
			if ( previewAbortRef.current ) {
				previewAbortRef.current.abort();
			}
		};
	}, [ loadPreview, visible ] );

	const [ exporting, setExporting ] = useState( false );
	const exportAbortRef = useRef( null );
	const mountedRef = useRef( true );

	// Abort an in-flight export request when the component unmounts so the
	// promise chain cannot update state on a dead component.
	useEffect( () => {
		mountedRef.current = true;
		return () => {
			mountedRef.current = false;
			if ( exportAbortRef.current ) {
				exportAbortRef.current.abort();
			}
		};
	}, [] );

	const handleExport = useCallback( () => {
		if ( exporting ) {
			return;
		}

		const controller = new AbortController();
		exportAbortRef.current = controller;

		// Hard client-side timeout: without it a hung backend leaves the
		// button stuck on "Exporting…" forever.
		const timeoutId = setTimeout( () => controller.abort(), EXPORT_TIMEOUT_MS );

		setExporting( true );
		setToast( {
			open: true,
			type: 'info',
			message: __( 'Generating export…', 'rawtype-order-exporter-woocommerce' ),
		} );

		const columns = getColumnsForExport;
		const nextExportOptions = { ...( exportOptions || {} ) };
		if ( 'xlsx' === exportFormat && 'single' !== multisheetMode ) {
			nextExportOptions.multisheet_mode = multisheetMode;
			if ( 'by_group' === multisheetMode ) {
				nextExportOptions.group_field = multisheetGroupField;
			} else {
				delete nextExportOptions.group_field;
			}
		} else {
			delete nextExportOptions.multisheet_mode;
			delete nextExportOptions.group_field;
		}

		const payload = {
			entity,
			filters,
			logic,
			columns,
			format: exportFormat,
			options: nextExportOptions,
			calculated_fields: calculatedFields,
		};

		if ( 'cross-entity' === entity ) {
			payload.cross_entity = crossEntityConfig || {};
		}

		apiFetch( {
			path: '/rawtype_oe/v1/export',
			method: 'POST',
			data: payload,
			signal: controller.signal,
		} )
			.then( ( result ) => {
				clearTimeout( timeoutId );
				if ( ! mountedRef.current ) {
					return;
				}
				setExporting( false );

				const toastMessage = result.truncated
					? sprintf(
						/* translators: 1: exported row count, 2: format */
						__( 'Export completed: first %1$d records in %2$s (free version limit). Upgrade to Pro to export everything.', 'rawtype-order-exporter-woocommerce' ),
						result.count || 0,
						( result.format || '' ).toUpperCase(),
					)
					: sprintf(
						/* translators: 1: row count, 2: format */
						__( 'Export completed: %1$d records in %2$s', 'rawtype-order-exporter-woocommerce' ),
						result.count || 0,
						( result.format || '' ).toUpperCase(),
					);

				setToast( {
					open: true,
					type: result.truncated ? 'warning' : 'success',
					message: toastMessage,
				} );

				// Trigger file download.
				if ( result.download_url ) {
					window.location.href = result.download_url;
				}
			} )
			.catch( ( err ) => {
				clearTimeout( timeoutId );
				if ( ! mountedRef.current ) {
					return;
				}
				setExporting( false );

				const timedOut = 'AbortError' === err.name;
				setToast( {
					open: true,
					type: 'error',
					message: timedOut
						? __( 'The export is taking too long and was cancelled. Add filters to reduce the data set and try again.', 'rawtype-order-exporter-woocommerce' )
						: err.message || __( 'Export error', 'rawtype-order-exporter-woocommerce' ),
				} );
			} );
	}, [ exporting, entity, filters, logic, exportFormat, exportOptions, getColumnsForExport, crossEntityConfig, multisheetMode, multisheetGroupField, calculatedFields ] );

	const total = previewData ? previewData.total : previewCount;
	const data = previewData ? previewData.data : [];
	const isCrossEntity = 'cross-entity' === entity;

	if ( isCrossEntity && ! crossEntityValid ) {
		return (
			<Card className="roe-data-preview">
				<CardContent>
					<Notice type="warning">
						{ __(
							'The selected combination is not valid. Adjust the entities in the filters step.',
							'rawtype-order-exporter-woocommerce',
						) }
					</Notice>
				</CardContent>
				<CardFooter className="roe-preview-actions">
					<Button type="button" variant="outline" onClick={ onPrev }>
						{ __( 'Previous', 'rawtype-order-exporter-woocommerce' ) }
					</Button>
				</CardFooter>
			</Card>
		);
	}

	// Only show full skeleton on first load (no previous data).
	// When refreshing (e.g. item_mode change), keep existing data visible.
	if ( ! previewData && ! error && ( loading || selectedColumns.length > 0 ) ) {
		return (
			<Card className="roe-data-preview">
				<CardContent>
					<PreviewSkeleton columnCount={ selectedColumns.length } />
					{ isCrossEntity && (
						<p className="roe-cross-preview-loading-text">
							{ __( 'Processing cross-entity query...', 'rawtype-order-exporter-woocommerce' ) }
						</p>
					) }
				</CardContent>
			</Card>
		);
	}

	if ( error ) {
		const errorMessage = error.message || __( 'Unknown error', 'rawtype-order-exporter-woocommerce' );

		return (
			<Card className="roe-data-preview">
				<CardContent>
					<Notice type="error">
						{ isCrossEntity
							? sprintf(
								/* translators: %s: technical query error details */
								__( 'The query is too complex. Try reducing the entities or adding more filters. (%s)', 'rawtype-order-exporter-woocommerce' ),
								errorMessage,
							)
							: sprintf(
								/* translators: %s: technical preview error details */
								__( 'Error loading preview: %s', 'rawtype-order-exporter-woocommerce' ),
								errorMessage,
							) }
					</Notice>
				</CardContent>
				<CardFooter className="roe-preview-actions">
					<Button type="button" variant="outline" onClick={ onPrev }>
						{ __( 'Previous', 'rawtype-order-exporter-woocommerce' ) }
					</Button>
				</CardFooter>
			</Card>
		);
	}

	// Gate on previewData: before the first fetch resolves there is nothing to
	// judge, and flashing "No results" while the request is in flight is wrong.
	if ( previewData && 0 === data.length && ! loading ) {
		return (
			<Card className="roe-data-preview">
				<CardContent>
					<EmptyState
						title={ __( 'No results', 'rawtype-order-exporter-woocommerce' ) }
						description={
							isCrossEntity
								? __( 'No records matching the cross conditions were found. Try broadening your filters.', 'rawtype-order-exporter-woocommerce' )
								: __( 'No records match your filters. Try broadening your search criteria.', 'rawtype-order-exporter-woocommerce' )
						}
						action={
							<Button type="button" variant="outline" onClick={ onPrev }>
								{ __( 'Edit filters', 'rawtype-order-exporter-woocommerce' ) }
							</Button>
						}
					/>
				</CardContent>
			</Card>
		);
	}

	return (
		<>
			<Card className="roe-data-preview">
				<CardHeader>
					<CardTitle>
						{ __( 'Preview & Export', 'rawtype-order-exporter-woocommerce' ) }
						{ total > 0 && (
							<Badge variant="default">
								{ sprintf(
									/* translators: %d: total record count */
									_n(
										'%d record',
										'%d records',
										total,
										'rawtype-order-exporter-woocommerce',
									),
									total,
								) }
							</Badge>
						) }
						</CardTitle>
					<CardDescription>
						{ sprintf(
							/* translators: 1: sample count, 2: total records */
							__( 'Showing %1$d records out of %2$d total. Review the data and select the export format.', 'rawtype-order-exporter-woocommerce' ),
							data.length,
							total || 0,
						) }
					</CardDescription>
				</CardHeader>

				<CardContent className="roe-preview-content">
					{ previewData?.row_limit > 0 && total > previewData.row_limit && (
						<Notice type="warning">
							{ sprintf(
								/* translators: 1: total records, 2: row limit */
								__( 'Your query returns %1$d records, but the free version is limited to %2$d. The export will include only the first %2$d records. Upgrade to Pro to export without limits.', 'rawtype-order-exporter-woocommerce' ),
								total,
								previewData.row_limit,
							) }
						</Notice>
					) }
					{ previewData?.limited && (
						<Notice type="info">
							{ sprintf(
								/* translators: %d: applied record limit */
								__( 'Results have been limited to %d records. Apply more filters to refine.', 'rawtype-order-exporter-woocommerce' ),
								previewData?.limit || 0,
							) }
						</Notice>
					) }

					<div className="roe-preview-table-wrap" style={ loading ? { opacity: 0.5, pointerEvents: 'none', transition: 'opacity 0.2s' } : undefined }>
						<Table.Root>
							<Table.Head>
								<Table.Row>
									{ selectedColumns.map( ( col ) => (
										<Table.HeaderCell key={ col.key }>
											{ col.customLabel || col.label }
										</Table.HeaderCell>
									) ) }
								</Table.Row>
							</Table.Head>
							<Table.Body>
								{ data.map( ( row, rowIndex ) => (
									<Table.Row key={ rowIndex }>
										{ selectedColumns.map( ( col ) => (
											<Table.Cell key={ col.key } title={
												row[ col.key ] != null
													? decodeHtmlEntities( String( row[ col.key ] ) )
													: ''
											}>
												{ row[ col.key ] != null
													? decodeHtmlEntities( String( row[ col.key ] ) )
													: '' }
											</Table.Cell>
										) ) }
									</Table.Row>
								) ) }
							</Table.Body>
						</Table.Root>
					</div>

					<Separator className="roe-preview-separator" />

					<div className="roe-preview-export-section">
						<CardTitle as="h4" className="roe-preview-export-title">
							{ __( 'Export options', 'rawtype-order-exporter-woocommerce' ) }
						</CardTitle>

						{ 'orders' === entity && (
							<div className="roe-item-mode-option">
								<Select.Root
									value={ exportOptions?.item_mode || 'concatenated' }
									onValueChange={ ( val ) => {
										onUpdateExportOptions?.( ( previous ) => ( {
											...( previous || {} ),
											item_mode: val,
										} ) );
									} }
								>
									<Select.Trigger
										placeholder={ __( 'Order line item mode', 'rawtype-order-exporter-woocommerce' ) }
									/>
									<Select.Content>
										<Select.Item value="concatenated">
											{ __( 'One row per order (items concatenated)', 'rawtype-order-exporter-woocommerce' ) }
										</Select.Item>
										<Select.Item value="expanded">
											{ __( 'One row per product (items expanded)', 'rawtype-order-exporter-woocommerce' ) }
										</Select.Item>
									</Select.Content>
								</Select.Root>
							</div>
						) }

						{ 'products' === entity && (
							<div className="roe-item-mode-option">
								<Select.Root
									value={ exportOptions?.item_mode || 'flat' }
									onValueChange={ ( val ) => {
										onUpdateExportOptions?.( ( previous ) => ( {
											...( previous || {} ),
											item_mode: val,
										} ) );
									} }
								>
									<Select.Trigger
										placeholder={ __( 'Variation mode', 'rawtype-order-exporter-woocommerce' ) }
									/>
									<Select.Content>
										<Select.Item value="flat">
											{ __( 'One row per product (no variations)', 'rawtype-order-exporter-woocommerce' ) }
										</Select.Item>
										<Select.Item value="expanded">
											{ __( 'One row per variation (expanded)', 'rawtype-order-exporter-woocommerce' ) }
										</Select.Item>
									</Select.Content>
								</Select.Root>
							</div>
						) }

						{ 'customers' === entity && (
							<div className="roe-item-mode-option">
								<Checkbox
									id="roe-include-guests"
									checked={ !! exportOptions?.include_guests }
									onCheckedChange={ ( checked ) => {
										onUpdateExportOptions?.( ( previous ) => ( {
											...( previous || {} ),
											include_guests: !! checked,
										} ) );
									} }
									label={ __( 'Include guest customers (guest checkouts)', 'rawtype-order-exporter-woocommerce' ) }
								/>
								{ !! exportOptions?.include_guests && filters.some( ( f ) => 'date' === f.type && 'date_registered' === f.field ) && (
									<Notice type="warning">
										{ __( 'Guest customers have no registration date and will be excluded by the registration date filter.', 'rawtype-order-exporter-woocommerce' ) }
									</Notice>
								) }
							</div>
						) }

						<div className="roe-preview-export-row">
							<Select.Root
								value={ exportFormat }
								onValueChange={ ( val ) => {
									onSetExportFormat( val );
									if ( 'xlsx' !== val ) {
										onUpdateExportOptions?.( ( previous ) => {
											const next = { ...( previous || {} ) };
											delete next.multisheet_mode;
											delete next.group_field;
											return next;
										} );
									}
								} }
							>
								<Select.Trigger
									placeholder={ __( 'Format', 'rawtype-order-exporter-woocommerce' ) }
								/>
								<Select.Content>
									<Select.Item value="csv">CSV</Select.Item>
									<Select.Item value="xlsx">XLSX</Select.Item>
								</Select.Content>
							</Select.Root>
							<Button type="button" onClick={ handleExport } disabled={ exporting }>
								{ exporting
									? __( 'Exporting…', 'rawtype-order-exporter-woocommerce' )
									: __( 'Export', 'rawtype-order-exporter-woocommerce' ) }
							</Button>
							{ onSaveProfile && (
								<Button
									type="button"
									variant="outline"
									onClick={ onSaveProfile }
								>
									{ __( 'Save as profile', 'rawtype-order-exporter-woocommerce' ) }
								</Button>
							) }
						</div>
						{ 'xlsx' === exportFormat && multisheetConfig && (
							<div className="roe-multisheet-options">
								<CardTitle as="h5">{ __( 'File structure', 'rawtype-order-exporter-woocommerce' ) }</CardTitle>
								<RadioGroup.Root
									value={ multisheetMode }
									onValueChange={ ( value ) => {
										onUpdateExportOptions?.( ( previous ) => {
											const next = { ...( previous || {} ) };
											if ( 'single' === value ) {
												delete next.multisheet_mode;
												delete next.group_field;
											} else {
												next.multisheet_mode = value;
												if ( 'by_group' !== value ) {
													delete next.group_field;
												}
											}
											return next;
										} );
									} }
								>
									{ availableModes.map( ( mode ) => (
										<RadioGroup.Item
											key={ mode }
											id={ `roe-multisheet-${ mode }` }
											value={ mode }
										>
											{ MODE_LABELS[ mode ] }
										</RadioGroup.Item>
									) ) }
								</RadioGroup.Root>
								{ 'by_group' === multisheetMode && (
									<div className="roe-multisheet-group-field">
										<Select.Root
											value={ multisheetGroupField }
											onValueChange={ ( value ) => {
												onUpdateExportOptions?.( ( previous ) => ( {
													...( previous || {} ),
													multisheet_mode: 'by_group',
													group_field: value,
												} ) );
											} }
										>
											<Select.Trigger
												placeholder={ __( 'Grouping field', 'rawtype-order-exporter-woocommerce' ) }
											/>
											<Select.Content>
												{ availableGroupFields.map( ( field ) => (
													<Select.Item
														key={ field.value }
														value={ field.value }
													>
														{ field.label }
													</Select.Item>
												) ) }
											</Select.Content>
										</Select.Root>
									</div>
								) }
							</div>
						) }
					</div>
				</CardContent>

				<CardFooter className="roe-preview-actions">
					<Button type="button" variant="outline" onClick={ onPrev }>
						{ __( 'Previous', 'rawtype-order-exporter-woocommerce' ) }
					</Button>
				</CardFooter>
			</Card>

			<Toast.Notification
				open={ toast.open }
				onOpenChange={ ( isOpen ) => {
					setToast( ( prev ) => ( { ...prev, open: isOpen } ) );
				} }
				type={ toast.type }
				message={ toast.message }
			/>
		</>
	);
}
