import {
	type AdminData,
	type AdminRecord,
	field,
	navigationForScreen,
	numberValue,
	safeArray,
	safeRecord,
	statusLabel,
} from './admin-data';
import { formatHistoryTime } from './history-screen-helpers';
import { launchDetailHref } from './launch-detail-route';

export interface ItemHistorySection {
	id: 'upcoming' | 'recent';
	title: string;
	empty: string;
	rows: AdminRecord[];
}

const finishedStatuses = [
	'success',
	'successful',
	'successful_with_warnings',
	'complete',
	'completed',
	'failed',
	'partially_failed',
	'partial_failed',
	'cancelled',
	'canceled',
	'rolled_back',
	'restored',
	'rollback_failed',
];

const statusKey = ( row: AdminRecord ): string =>
	field( row, 'lastRunStatus', field( row, 'status', 'unknown' ) )
		.toLowerCase()
		.replace( /[\s-]+/g, '_' );

const plural = ( count: number, singular: string, many: string ): string =>
	1 === count ? singular : many;

const firstHistoryTime = ( row: AdminRecord, keys: string[] ): string => {
	for ( const key of keys ) {
		const value = field( row, key );

		if ( value ) {
			return value;
		}
	}

	return '';
};

export const itemTargetKey = ( target: AdminRecord, index = 0 ): string =>
	field(
		target,
		'targetReference',
		field( target, 'reference', field( target, 'id', `item-${ index }` ) )
	);

export const targetHistoryRows = ( target: AdminRecord ): AdminRecord[] =>
	safeArray< AdminRecord >( target.history );

export const itemHistoryIsUpcoming = ( row: AdminRecord ): boolean =>
	! finishedStatuses.includes( statusKey( row ) );

export const itemHistorySections = (
	rows: AdminRecord[]
): ItemHistorySection[] => [
	{
		id: 'upcoming',
		title: 'Upcoming scheduled changes',
		empty: 'No upcoming scheduled changes for this item.',
		rows: rows.filter( itemHistoryIsUpcoming ),
	},
	{
		id: 'recent',
		title: 'Recent history for this item',
		empty: 'Completed, failed, cancelled, and restored changes will appear here.',
		rows: rows.filter( ( row ) => ! itemHistoryIsUpcoming( row ) ),
	},
];

export const itemHistoryTimeLabel = (
	row: AdminRecord,
	timezone: string
): string => {
	const status = statusKey( row );
	const completedAt = firstHistoryTime( row, [
		'completedAt',
		'endAt',
		'updatedAt',
	] );
	const goLiveAt = firstHistoryTime( row, [
		'startAt',
		'scheduledAt',
		'createdAt',
	] );

	if (
		[
			'success',
			'successful',
			'successful_with_warnings',
			'complete',
			'completed',
		].includes( status )
	) {
		return `Completed ${ formatHistoryTime(
			completedAt || goLiveAt,
			timezone
		) }`;
	}

	if (
		[ 'failed', 'partially_failed', 'partial_failed' ].includes( status )
	) {
		return `Needs review ${ formatHistoryTime(
			completedAt || goLiveAt,
			timezone
		) }`;
	}

	if ( [ 'cancelled', 'canceled' ].includes( status ) ) {
		return `Cancelled ${ formatHistoryTime(
			completedAt || goLiveAt,
			timezone
		) }`;
	}

	if ( [ 'rolled_back', 'restored' ].includes( status ) ) {
		return `Restored ${ formatHistoryTime(
			completedAt || goLiveAt,
			timezone
		) }`;
	}

	if ( 'rollback_failed' === status ) {
		return `Restore needs review ${ formatHistoryTime(
			completedAt || goLiveAt,
			timezone
		) }`;
	}

	if ( 'running' === status ) {
		return 'Applying now';
	}

	if ( ! goLiveAt ) {
		return 'Not scheduled yet';
	}

	return `Goes live ${ formatHistoryTime( goLiveAt, timezone ) }`;
};

export const itemHistorySummary = ( row: AdminRecord ): string => {
	const status = statusKey( row );

	if ( 'running' === status ) {
		return 'This item is being updated now.';
	}

	if (
		[ 'success', 'successful', 'complete', 'completed' ].includes( status )
	) {
		return 'Updated this item successfully.';
	}

	if ( 'successful_with_warnings' === status ) {
		return 'Updated this item, with something to review.';
	}

	if (
		[ 'failed', 'partially_failed', 'partial_failed' ].includes( status )
	) {
		return 'Needs review before trying again.';
	}

	if ( [ 'cancelled', 'canceled' ].includes( status ) ) {
		return 'Cancelled before changing this item.';
	}

	if ( [ 'rolled_back', 'restored' ].includes( status ) ) {
		return 'Previous values were restored for this item.';
	}

	if ( 'rollback_failed' === status ) {
		return 'Previous values could not be restored automatically.';
	}

	return 'Will update this item when the change goes live.';
};

export const targetHistorySummary = ( target: AdminRecord ): string => {
	const failed = numberValue( target.failedCount );

	if ( failed > 0 ) {
		return `${ failed } ${ plural(
			failed,
			'change needs',
			'changes need'
		) } review for this item.`;
	}

	const upcoming = numberValue( target.upcomingCount );

	if ( upcoming > 0 ) {
		return `${ upcoming } upcoming ${ plural(
			upcoming,
			'change is',
			'changes are'
		) } scheduled for this item.`;
	}

	const historyCount = targetHistoryRows( target ).length;

	if ( historyCount > 0 ) {
		return `${ historyCount } recent ${ plural(
			historyCount,
			'change has',
			'changes have'
		) } affected this item.`;
	}

	return 'No scheduled changes are attached to this item yet.';
};

export const targetHistoryStatusValue = ( target: AdminRecord ): string => {
	if ( numberValue( target.failedCount ) > 0 ) {
		return 'warning';
	}

	if ( numberValue( target.upcomingCount ) > 0 ) {
		return 'scheduled';
	}

	return targetHistoryRows( target ).length > 0 ? 'completed' : 'ready';
};

export const targetNextChangeLabel = (
	target: AdminRecord,
	timezone: string
): string => {
	const nextChange = field( target, 'nextChange' );

	return nextChange
		? formatHistoryTime( nextChange, timezone )
		: 'No upcoming date';
};

export const itemHistoryTimezone = ( data: AdminData ): string =>
	field( safeRecord( data.scheduleDefaults ), 'timezone', 'UTC' );

export const scheduledChangeHref = (
	data: AdminData,
	launchUuid: string
): string => {
	const changeScreen = navigationForScreen(
		safeArray( data.navigation ),
		'all-changes'
	);
	const href = field( changeScreen || {}, 'href', '#' );

	return launchUuid ? launchDetailHref( href, launchUuid ) : href;
};

export const itemHistoryStatusLabel = ( row: AdminRecord ): string =>
	statusLabel( statusKey( row ) );
