// @flow
import * as React from 'react';
import {
	Container,
	Row,
	Col,
	ListGroup,
	ListGroupItem,
	Button,
	ButtonGroup,
	Card,
	CardHeader,
	CardBody,
	Badge,
	Modal, ModalHeader, ModalBody, ModalFooter,
} from 'reactstrap';

import {
	Route,
	Link
} from 'react-router-dom';

import { connect } from 'react-redux';

import * as Selectors from '@store/selectors';
import * as Actions from '@store/actions';

import { BEAT_ENTITIES, getTcFromExpName } from '@helpers/beat';
import type { BeatEntity, BeatObject } from '@helpers/beat';

import ValidSchemaBadge from './ValidSchemaBadge.jsx';

import NewEntityModal from './NewEntityModal.jsx';

type ElProps = {
	data: BeatObject,
	entity: BeatEntity,
	copyFunc: () => any,
	deleteFunc: () => any,
	versionFunc?: () => any,
};

const El = ({ data, entity, copyFunc, deleteFunc, versionFunc }: ElProps) => (
	<ListGroupItem className='d-flex align-items-center'>
		<span>
			<Link to={`/${ entity }/${ data.name }`}>
				{ data.name }
			</Link>
			<ValidSchemaBadge entity={entity} obj={data} />
			<small> { data.contents['description'] || data.contents['#description'] }</small>
		</span>

		<div className='ml-auto'>
			{ versionFunc && <Button outline color='success' onClick={() => versionFunc()}>New Version</Button> }{' '}
			<Button outline color='success' onClick={() => copyFunc()}>Clone</Button>{' '}
			<Button outline color='danger' onClick={() => deleteFunc()}>Delete</Button>
		</div>
	</ListGroupItem>
);

type Props = {
	entity: BeatEntity,
	data: BeatObject[],
	// deleting toolchains should show the exps that will be deleted in the popup
	experiments: BeatObject[],
	deleteFunc: (BeatObject, ?BeatEntity) => () => any,
};

type State = {
	newModalOpen: boolean,
	copyObj: {},
	deleteModalData: false | BeatObject,
	nameOrVersion: boolean,
};

export class EntityList extends React.Component<Props, State> {
	constructor (props: Props) {
		super(props);
	}

	state = {
		newModalOpen: false,
		copyObj: {},
		deleteModalData: false,
		nameOrVersion: false,
	}

	toggleDeleteModal = (e: false | BeatObject = false) => this.setState({ deleteModalData: e || false });

	toggleModal = () => this.setState({ newModalOpen: !this.state.newModalOpen });

	copyFunc = (obj: BeatObject) => this.setState({ copyObj: obj, newModalOpen: true, nameOrVersion: true });

	versionFunc = (obj: BeatObject) => this.setState({ copyObj: obj, newModalOpen: true, nameOrVersion: false });

	render () {
		// name to be deleted if any
		const deleteName = this.state.deleteModalData ? this.state.deleteModalData.name : '';

		// need to find which objects can be versioned (which objects have no newer versions)
		let versionableNames;
		if(this.props.entity === 'experiment')
			versionableNames = [];
		else {
			const getNameAndVersion = name => {
				const segs = name.split('/');
				const n = segs.slice(0, -1).join('/');
				const version = Number.parseInt(segs[segs.length - 1]);
				return [n, version];
			};
			// sort the names so that all versions of an object are together,
			// and the versions are sorted from first to laste
			const sortedNames = this.props.data.map(d => d.name).sort();
			const nameAndVersions = sortedNames.map(n => getNameAndVersion(n));
			// find the complete names of the highest versions of each obj
			// since we sorted we just find the last version of each obj
			versionableNames = sortedNames.filter((n, i) => {
				if(i === sortedNames.length - 1)
					return true;
				if(nameAndVersions[i + 1][0] === nameAndVersions[i][0])
					return false;
				return true;
			});
		}

		return (
			<React.Fragment>
				<ListGroup>
					<NewEntityModal
						toggle={this.toggleModal}
						isOpen={this.state.newModalOpen}
						entity={this.props.entity}
						copyObj={this.state.copyObj}
						nameOrVersion={this.state.nameOrVersion}
					/>
					{ this.props.data.map((d, i) =>
						<El
							key={i}
							data={d}
							entity={this.props.entity}
							copyFunc={() => this.copyFunc(d)}
							versionFunc={versionableNames.includes(d.name) ? () => this.versionFunc(d) : undefined}
							deleteFunc={() => this.toggleDeleteModal(d)}
						/>)
					}
				</ListGroup>
				<Modal
					toggle={() => this.toggleDeleteModal()}
					isOpen={this.state.deleteModalData !== false}
				>
					<ModalHeader toggle={() => this.toggleDeleteModal()}>Confirm Deletion</ModalHeader>
					<ModalBody>
						Are you sure you want to permanently delete{' '}
						<pre className='preInline'>
							{ deleteName }
						</pre>?
						This will delete the object from your BEAT prefix as well as the web app.
						{
							this.props.entity === 'toolchain' &&
								<React.Fragment>
									<p>Deleting this toolchain will also delete the following experiments that depend on it:</p>
									<ListGroup>
										{
											this.props.experiments
											.filter(e => getTcFromExpName(e.name) === deleteName)
											.map((e, i) => <ListGroupItem key={i}>{ e.name }</ListGroupItem>)
										}
									</ListGroup>
								</React.Fragment>
						}
					</ModalBody>
					<ModalFooter>
						<Button color='danger'
							onClick={async () => {
								if(this.state.deleteModalData){
									if(this.props.entity === 'toolchain'){
										await Promise.all(
											this.props.experiments
											.filter(e => getTcFromExpName(e.name) === deleteName)
											.map((e) => this.props.deleteFunc(e, 'experiment'))
										);
									}
									await this.props.deleteFunc(this.state.deleteModalData);
								}
								this.toggleDeleteModal();
							}}
						>Delete</Button>
						<Button color='secondary'
							autoFocus
							onClick={() => this.toggleDeleteModal()}
						>Cancel</Button>
					</ModalFooter>
				</Modal>
			</React.Fragment>
		);
	}
}

const mapStateToProps = (state, ownProps) => ({
	data: Selectors[`${ ownProps.entity }Get`](state),
	experiments: Selectors.experimentGet(state),
});

const mapDispatchToProps = (dispatch, ownProps) => ({
	deleteFunc: (obj, entity = ownProps.entity) => dispatch(Actions.deleteObject(entity, obj)()),
});

export default connect(mapStateToProps, mapDispatchToProps)(EntityList);
