/**
 * "Historial" admin screen: every job with its status/totals, and per-job
 * view/rollback actions (SPEC §6).
 */

import { Fragment, useCallback, useEffect, useState } from '@wordpress/element';
import {
	Button,
	Card,
	CardBody,
	CardHeader,
	Modal,
	Notice,
	Spinner,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';

import { getJobProgress, listJobs, listSuppliers, rollbackJob } from '../api';

const ROLLBACK_POLL_INTERVAL_MS = 1500;

export default function HistoryScreen() {
	const [ jobs, setJobs ] = useState( [] );
	const [ suppliersById, setSuppliersById ] = useState( {} );
	const [ isLoading, setIsLoading ] = useState( true );
	const [ error, setError ] = useState( '' );
	const [ expandedJobId, setExpandedJobId ] = useState( null );
	const [ rollingBackJobId, setRollingBackJobId ] = useState( null );
	const [ rollbackCandidate, setRollbackCandidate ] = useState( null );
	const [ page, setPage ] = useState( 1 );
	const [ total, setTotal ] = useState( 0 );
	const [ perPage, setPerPage ] = useState( 20 );

	const loadJobs = useCallback( () => {
		setIsLoading( true );
		return listJobs( page, perPage )
			.then( ( result ) => {
				setIsLoading( false );
				setJobs( result.jobs );
				setTotal( result.total );
				setPerPage( result.per_page );
			} )
			.catch( ( loadError ) => {
				setIsLoading( false );
				setError(
					loadError?.message ||
						__( 'Could not load the job history.', 'emberprice' )
				);
			} );
	}, [ page, perPage ] );

	useEffect( () => {
		loadJobs();
		listSuppliers()
			.then( ( suppliers ) => {
				setSuppliersById(
					suppliers.reduce(
						( byId, supplier ) => ( {
							...byId,
							[ supplier.id ]: supplier.name,
						} ),
						{}
					)
				);
			} )
			.catch( ( suppliersError ) => {
				setError(
					suppliersError?.message ||
						__( 'Could not load suppliers.', 'emberprice' )
				);
			} );
	}, [ loadJobs ] );

	const pollRollbackProgress = ( jobId ) => {
		getJobProgress( jobId )
			.then( ( progress ) => {
				if ( 'rolled_back' === progress.stage ) {
					setRollingBackJobId( null );
					loadJobs();
					return;
				}

				if ( 'failed' === progress.stage || progress.stalled ) {
					setRollingBackJobId( null );
					setError(
						progress.message ||
							progress.error?.message ||
							__(
								'Rollback stopped before it completed.',
								'emberprice'
							)
					);
					loadJobs();
					return;
				}

				setTimeout(
					() => pollRollbackProgress( jobId ),
					ROLLBACK_POLL_INTERVAL_MS
				);
			} )
			.catch( ( progressError ) => {
				setRollingBackJobId( null );
				setError(
					progressError?.message ||
						__( 'Could not read rollback progress.', 'emberprice' )
				);
			} );
	};

	const handleRollback = ( job ) => {
		setRollbackCandidate( job );
	};

	const confirmRollback = () => {
		if ( ! rollbackCandidate ) {
			return;
		}

		const job = rollbackCandidate;
		const jobId = job.id;
		setRollbackCandidate( null );
		setError( '' );
		setRollingBackJobId( jobId );

		rollbackJob( jobId, Boolean( job.totals?.rollback ) )
			.then( () => pollRollbackProgress( jobId ) )
			.catch( ( rollbackError ) => {
				setRollingBackJobId( null );
				setError(
					rollbackError?.message ||
						__( 'Could not roll back this job.', 'emberprice' )
				);
			} );
	};

	const totalPages = Math.max( 1, Math.ceil( total / perPage ) );

	if ( isLoading ) {
		return (
			<Card className="embp-history-screen">
				<CardBody>
					<Spinner />
				</CardBody>
			</Card>
		);
	}

	return (
		<Card className="embp-history-screen">
			<CardHeader>
				<h2>{ __( 'History', 'emberprice' ) }</h2>
			</CardHeader>
			<CardBody>
				{ error && (
					<Notice
						status="error"
						isDismissible
						onRemove={ () => setError( '' ) }
					>
						{ error }
					</Notice>
				) }
				{ rollbackCandidate && (
					<Modal
						title={ __( 'Confirm rollback', 'emberprice' ) }
						onRequestClose={ () => setRollbackCandidate( null ) }
					>
						<p>
							{ __(
								'Roll back this import? WooCommerce prices will be restored from its snapshot.',
								'emberprice'
							) }
						</p>
						<div className="embp-history-confirm-actions">
							<Button
								variant="tertiary"
								onClick={ () => setRollbackCandidate( null ) }
							>
								{ __( 'Cancel', 'emberprice' ) }
							</Button>
							<Button
								variant="primary"
								onClick={ confirmRollback }
							>
								{ __( 'Rollback', 'emberprice' ) }
							</Button>
						</div>
					</Modal>
				) }

				{ 0 === jobs.length ? (
					<p>{ __( 'No jobs yet.', 'emberprice' ) }</p>
				) : (
					<div className="embp-history-table-wrap">
						<table className="embp-history-table">
							<thead>
								<tr>
									<th scope="col">
										{ __( 'File', 'emberprice' ) }
									</th>
									<th scope="col">
										{ __( 'Supplier', 'emberprice' ) }
									</th>
									<th scope="col">
										{ __( 'Status', 'emberprice' ) }
									</th>
									<th scope="col">
										{ __( 'Created', 'emberprice' ) }
									</th>
									<th scope="col">
										{ __( 'Actions', 'emberprice' ) }
									</th>
								</tr>
							</thead>
							<tbody>
								{ jobs.map( ( job ) => (
									<Fragment key={ job.id }>
										<tr>
											<td>{ job.file_name }</td>
											<td>
												{ suppliersById[
													job.supplier_id
												] || job.supplier_id }
											</td>
											<td>
												{ job.id === rollingBackJobId
													? __(
															'Rolling back…',
															'emberprice'
													  )
													: job.status }
											</td>
											<td>{ job.created_at }</td>
											<td className="embp-history-table__actions">
												<Button
													variant="secondary"
													onClick={ () =>
														setExpandedJobId(
															expandedJobId ===
																job.id
																? null
																: job.id
														)
													}
												>
													{ __(
														'View',
														'emberprice'
													) }
												</Button>
												{ [
													'mapping',
													'matching',
													'review',
													'ready',
													'applying',
													'failed',
												].includes( job.status ) && (
													<Button
														variant="primary"
														href={ `admin.php?page=emberprice&job_id=${ job.id }` }
													>
														{ __(
															'Continue',
															'emberprice'
														) }
													</Button>
												) }
												{ ( 'applied' === job.status ||
													( 'failed' === job.status &&
														[
															'apply',
															'rollback',
														].includes(
															job.totals?.error
																?.stage
														) ) ) && (
													<Button
														variant="secondary"
														isBusy={
															job.id ===
															rollingBackJobId
														}
														disabled={
															null !==
															rollingBackJobId
														}
														onClick={ () =>
															handleRollback(
																job
															)
														}
													>
														{ __(
															'Rollback',
															'emberprice'
														) }
													</Button>
												) }
											</td>
										</tr>
										{ expandedJobId === job.id && (
											<tr>
												<td colSpan={ 5 }>
													<div className="embp-history-details">
														<p>
															<strong>
																{ __(
																	'File hash:',
																	'emberprice'
																) }
															</strong>{ ' ' }
															<code>
																{
																	job.file_hash
																}
															</code>
														</p>
														<p>
															<strong>
																{ __(
																	'Rows:',
																	'emberprice'
																) }
															</strong>{ ' ' }
															{ Object.entries(
																job.totals || {}
															)
																.filter(
																	( [
																		,
																		value,
																	] ) =>
																		'number' ===
																		typeof value
																)
																.map(
																	( [
																		key,
																		value,
																	] ) =>
																		`${ key }: ${ value }`
																)
																.join(
																	' · '
																) || '—' }
														</p>
														{ job.totals?.apply && (
															<p>
																<strong>
																	{ __(
																		'Application:',
																		'emberprice'
																	) }
																</strong>{ ' ' }
																{ sprintf(
																	/* translators: 1: processed rows, 2: total rows, 3: failed rows. */
																	__(
																		'%1$d of %2$d processed; %3$d failed.',
																		'emberprice'
																	),
																	job.totals
																		.apply
																		.done ||
																		0,
																	job.totals
																		.apply
																		.total ||
																		0,
																	job.totals
																		.apply
																		.failed ||
																		0
																) }
															</p>
														) }
														{ job.totals?.error
															?.message && (
															<Notice
																status="error"
																isDismissible={
																	false
																}
															>
																{
																	job.totals
																		.error
																		.message
																}
															</Notice>
														) }
													</div>
												</td>
											</tr>
										) }
									</Fragment>
								) ) }
							</tbody>
						</table>
					</div>
				) }
				{ totalPages > 1 && (
					<div className="embp-history-pagination">
						<Button
							variant="secondary"
							disabled={ page <= 1 }
							onClick={ () =>
								setPage( ( current ) => current - 1 )
							}
						>
							{ __( 'Previous', 'emberprice' ) }
						</Button>
						<span>
							{ sprintf(
								/* translators: 1: current page, 2: total pages. */
								__( 'Page %1$d of %2$d', 'emberprice' ),
								page,
								totalPages
							) }
						</span>
						<Button
							variant="secondary"
							disabled={ page >= totalPages }
							onClick={ () =>
								setPage( ( current ) => current + 1 )
							}
						>
							{ __( 'Next', 'emberprice' ) }
						</Button>
					</div>
				) }
			</CardBody>
		</Card>
	);
}
