/* eslint-disable max-lines */
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';

import chai, { expect } from 'chai';

import Dropdown from '../../menu-dropdown';
import DataTable from '../../data-table';
import DataTableColumn from '../../data-table/column';
import DataTableRowActions from '../../data-table/row-actions';
import DataTableHighlightCell from '../../data-table/highlight-cell';
import IconSettings from '../../icon-settings';

chai.should();

const {
	Simulate,
	scryRenderedComponentsWithType,
	findRenderedDOMComponentWithClass,
} = TestUtils;

describe('DataTable: ', function describeFunction() {
	const items = [
		{
			id: '8IKZHZZV80',
			name: 'Cloudhub',
			count: 100976,
			lastModified: 'Yesterday',
		},
		{
			id: '5GJOOOPWU7',
			name: 'Cloudhub + Anypoint Connectors',
			count: 54976,
			lastModified: 'Today',
		},
		{
			id: 'Q8Z71ZUCEZ',
			name: 'Cloud City',
			count: 101280,
			lastModified: 'Today',
		},
		{
			id: '2FSH2DP0LY',
			name: 'IoT',
			count: 976,
			lastModified: 'Yesterday',
		},
		{
			id: '8NE888QKV1',
			name: 'IoT + Anypoint Connectors',
			count: 54976,
			lastModified: 'Today',
		},
		{
			id: 'M4D37GW83H',
			name: 'Salesforce Tower',
			count: 101280,
			lastModified: 'Today',
		},
	];

	const columns = [
		{
			label: 'Name',
			property: 'name',
			truncate: true,
		},
		{
			label: 'Count',
			property: 'count',
			sortable: true,
		},
	];

	const defaultProps = {
		id: 'DataTableExample-default',
		items,
		selectRows: true,
	};

	const renderTable = (instance) =>
		function renderTableFunction() {
			this.dom = document.createElement('div');
			document.body.appendChild(this.dom);
			this.component = ReactDOM.render(
				<IconSettings iconPath="/assets/icons">{instance}</IconSettings>,
				this.dom
			);
		};

	function removeTable() {
		ReactDOM.unmountComponentAtNode(this.dom);
		document.body.removeChild(this.dom);
	}

	const getTable = (dom) => dom.querySelector('.slds-table');

	const getRow = (dom, row) => {
		const tbody = getTable(dom).querySelectorAll('tbody')[0];
		return tbody.querySelectorAll('tr')[row - 1];
	};

	const getCell = (dom, row, column) => {
		const tr = getRow(dom, row);
		return tr.querySelectorAll('td')[column];
	};

	const getMenu = (dom) => dom.querySelector('.slds-dropdown');

	describe('Structure', function describeFunction2() {
		beforeEach(
			renderTable(
				<DataTable {...defaultProps}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			)
		);

		afterEach(removeTable);

		it('has a header', function() {
			const thead = getTable(this.dom).querySelectorAll('thead');
			thead.should.have.length(1);
			thead[0].querySelectorAll('th').should.have.length(3);
		});

		it('has a row for each item', function() {
			const tbody = getTable(this.dom).querySelectorAll('tbody');
			tbody.should.have.length(1);
			tbody[0].querySelectorAll('tr').should.have.length(6);
		});

		it('renders the correct contents in each cell', function() {
			const firstName = getCell(this.dom, 1, 1);
			firstName.innerHTML.should.equal(
				'<div class="" title="Cloudhub">Cloudhub</div>'
			);
			const secondCount = getCell(this.dom, 2, 2);
			secondCount.innerHTML.should.equal(
				'<div class="" title="54976">54976</div>'
			);
		});

		it('has checkboxes when selectRows is true or "checkbox"', function() {
			let checkboxes = getTable(this.dom).querySelectorAll('.slds-checkbox');
			checkboxes.should.have.length(7);
			removeTable.call(this);

			renderTable(
				<DataTable {...defaultProps} selectRows={false}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);
			checkboxes = getTable(this.dom).querySelectorAll('.slds-checkbox');
			checkboxes.should.have.length(0);
			removeTable.call(this);

			renderTable(
				<DataTable {...defaultProps} selectRows="checkbox">
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);
			checkboxes = getTable(this.dom).querySelectorAll('.slds-checkbox');
			checkboxes.should.have.length(7);
		});

		it('has radios only when selectRows is "radio"', function() {
			const checkboxes = getTable(this.dom).querySelectorAll('.slds-checkbox');
			checkboxes.should.have.length(7);
			removeTable.call(this);

			renderTable(
				<DataTable {...defaultProps} selectRows="radio">
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);
			const radios = getTable(this.dom).querySelectorAll('.slds-radio');
			radios.should.have.length(6);
		});
	});

	describe('Selectable - Checkbox', function describeFunction2() {
		const defaultSelection = [
			{
				id: '8IKZHZZV80',
				name: 'Cloudhub',
				count: 100976,
				lastModified: 'Yesterday',
			},
		];

		afterEach(removeTable);

		it('can start with a row selected', function() {
			renderTable(
				<DataTable {...defaultProps} selection={defaultSelection}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const tbody = getTable(this.dom).querySelectorAll('tbody')[0];
			const selectedRows = tbody.querySelectorAll('tr.slds-is-selected');
			selectedRows.should.have.length(1);
			const checkedBoxes = tbody.querySelectorAll(
				'.slds-checkbox input:checked'
			);
			checkedBoxes.should.have.length(1);
		});

		it('can deselect a row', function(done) {
			this.onRowChange = (event, { selection }) => {
				selection.should.have.length(0);
				done();
			};

			renderTable(
				<DataTable
					{...defaultProps}
					selection={defaultSelection}
					onRowChange={this.onRowChange}
				>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const tbody = getTable(this.dom).querySelectorAll('tbody')[0];
			const selectedRow = tbody.querySelectorAll('tr.slds-is-selected')[0];
			const checkbox = selectedRow.querySelectorAll('.slds-checkbox input')[0];
			Simulate.change(checkbox, { target: { checked: false } });
		});

		it('can select a row', function(done) {
			this.onRowChange = (event, { selection }) => {
				selection.should.have.length(2);
				selection[1].id.should.equal('5GJOOOPWU7');
				done();
			};

			renderTable(
				<DataTable
					{...defaultProps}
					selection={defaultSelection}
					onRowChange={this.onRowChange}
				>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const secondRow = getRow(this.dom, 2);
			const checkbox = secondRow.querySelectorAll('.slds-checkbox input')[0];

			Simulate.change(checkbox, { target: { checked: true } });
		});

		it('can select all rows', function(done) {
			this.onRowChange = (event, { selection }) => {
				selection.should.have.length(6);
				done();
			};

			renderTable(
				<DataTable {...defaultProps} onRowChange={this.onRowChange}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const thead = getTable(this.dom).querySelectorAll('thead')[0];
			const checkAll = thead.querySelectorAll('.slds-checkbox input')[0];

			Simulate.change(checkAll, { target: { checked: true } });
		});

		it('can deselect all rows', function(done) {
			this.onRowChange = (event, { selection }) => {
				selection.should.have.length(0);
				done();
			};

			renderTable(
				<DataTable
					{...defaultProps}
					selection={items}
					onRowChange={this.onRowChange}
				>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const thead = getTable(this.dom).querySelectorAll('thead')[0];
			const checkAll = thead.querySelectorAll('.slds-checkbox input')[0];

			Simulate.change(checkAll, { target: { checked: false } });
		});
	});

	describe('Selectable - Radio', function describeFunction2() {
		const defaultSelection = [
			{
				id: '8IKZHZZV80',
				name: 'Cloudhub',
				count: 100976,
				lastModified: 'Yesterday',
			},
		];

		afterEach(removeTable);

		it('can start with a row selected', function() {
			renderTable(
				<DataTable
					{...defaultProps}
					selection={defaultSelection}
					selectRows="radio"
				>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const tbody = getTable(this.dom).querySelectorAll('tbody')[0];
			const selectedRows = tbody.querySelectorAll('tr.slds-is-selected');
			selectedRows.should.have.length(1);
			const radios = tbody.querySelectorAll('.slds-radio input:checked');
			radios.should.have.length(1);
		});

		it('can select a row', function(done) {
			this.onRowChange = (event, { selection }) => {
				selection.should.have.length(1);
				selection[0].id.should.equal('5GJOOOPWU7');
				done();
			};

			renderTable(
				<DataTable
					{...defaultProps}
					selection={defaultSelection}
					selectRows="radio"
					onRowChange={this.onRowChange}
				>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const secondRow = getRow(this.dom, 2);
			const radio = secondRow.querySelectorAll('.slds-radio input')[0];

			Simulate.change(radio, { target: { checked: true } });
		});
	});

	describe('Sortable', function describeFunction2() {
		afterEach(removeTable);

		it('first clicked on sortable column header should result in ascending sort by default', function(done) {
			this.onSort = (data) => {
				data.property.should.equal('count');
				data.sortDirection.should.equal('asc');
				done();
			};

			renderTable(
				<DataTable {...defaultProps} fixedLayout onSort={this.onSort}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const thead = getTable(this.dom).querySelectorAll('thead')[0];
			const sortButton = thead.querySelectorAll('a')[0];

			Simulate.click(sortButton, {});
		});

		it('if isDefaultSortDescending is true, first click on sortable column header should result in descending order', function(done) {
			this.onSort = (data) => {
				data.property.should.equal('count');
				data.sortDirection.should.equal('desc');
				done();
			};

			renderTable(
				<DataTable {...defaultProps} fixedLayout onSort={this.onSort}>
					{columns.map((columnProps) => (
						<DataTableColumn
							{...columnProps}
							isDefaultSortDescending
							key={columnProps.property}
						/>
					))}
				</DataTable>
			).call(this);

			const thead = getTable(this.dom).querySelectorAll('thead')[0];
			const sortButton = thead.querySelectorAll('a')[0];

			Simulate.click(sortButton, {});
		});

		it('does not call onSort when a non-sortable column is clicked', function(done) {
			this.onSort = () => {
				done('sort called');
			};

			renderTable(
				<DataTable {...defaultProps} onSort={this.onSort}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
				</DataTable>
			).call(this);

			const thead = getTable(this.dom).querySelectorAll('thead')[0];
			const secondColumn = thead.querySelectorAll('th')[1];

			Simulate.click(secondColumn, {});
			setTimeout(done, 0);
		});
	});

	describe('w/ RowActions', function describeFunction2() {
		afterEach(removeTable);

		it('renders the RowActions and uses dropdown override property', function() {
			renderTable(
				<DataTable {...defaultProps}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
					<DataTableRowActions
						dropdown={
							<Dropdown
								options={[
									{
										id: 0,
										label: 'Add to Group',
										value: '1',
									},
									{
										id: 1,
										label: 'Publish',
										value: '2',
									},
								]}
							/>
						}
					/>
				</DataTable>
			).call(this);

			const rowActionMenus = scryRenderedComponentsWithType(
				this.component,
				DataTableRowActions
			);
			rowActionMenus.should.have.length(6);
		});

		it('calls onAction & onSelect when an action is clicked', function(done) {
			let expectedCalbacks = 2;

			this.onAction = (item, action) => {
				item.id.should.equal('8IKZHZZV80');
				action.value.should.equal('1');
				// eslint-disable-next-line no-plusplus
				if (!--expectedCalbacks) done();
			};

			this.onSelect = (action) => {
				action.value.should.equal('1');
				// eslint-disable-next-line no-plusplus
				if (!--expectedCalbacks) done();
			};

			renderTable(
				<DataTable {...defaultProps}>
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property} />
					))}
					<DataTableRowActions
						onAction={this.onAction}
						options={[
							{
								id: 0,
								label: 'Add to Group',
								value: '1',
							},
							{
								id: 1,
								label: 'Publish',
								value: '2',
							},
						]}
						dropdown={<Dropdown onSelect={this.onSelect} />}
					/>
				</DataTable>
			).call(this);

			const rowActionMenu = scryRenderedComponentsWithType(
				this.component,
				DataTableRowActions
			)[0];
			const trigger = findRenderedDOMComponentWithClass(
				rowActionMenu,
				'slds-button'
			);
			Simulate.click(trigger, {});

			setTimeout(() => {
				const menu = getMenu(document.body);
				const firstAction = menu.querySelectorAll('.slds-dropdown__item')[0];
				Simulate.click(firstAction.querySelector('a'), {});
			}, 0);
		});
	});

	describe('w/ HighlightCell', function describeFunction2() {
		afterEach(removeTable);

		it('marks the appropriate text in a cell', function() {
			renderTable(
				<DataTable {...defaultProps} search="Cloud">
					{columns.map((columnProps) => (
						<DataTableColumn {...columnProps} key={columnProps.property}>
							<DataTableHighlightCell />
						</DataTableColumn>
					))}
				</DataTable>
			).call(this);

			const secondRow = getRow(this.dom, 2);
			const markedText = secondRow.querySelectorAll('mark')[0];
			markedText.innerHTML.should.equal('Cloud');
		});
	});

	describe('w/ Fixed Headers', function describeFunction2() {
		afterEach(removeTable);

		it('Renders a fixedHeader table as expected', function() {
			renderTable(
				<DataTable
					{...defaultProps}
					fixedHeader
					fixedLayout
					id="DataTable-FixedHeader-Test"
					onFixedHeaderResize={(event, data) => {
						expect(Array.isArray(data.headerRefs)).to.eql(true);
						expect(data.headerRefs.length).to.eql(4);
						data.headerRefs.forEach((ref) => {
							expect(typeof ref).to.eql('object');
						});
						expect(typeof data.scrollerRef).to.eql('object');
						expect(
							data.scrollerRef.className.search(
								'slds-table_header-fixed_scroller'
							) >= 0
						).to.eql(true);
					}}
					onToggleFixedHeaderListeners={(event, data) => {
						expect(typeof data.attach).to.eql('boolean');
						expect(typeof data.resizeHandler).to.eql('function');
						expect(typeof data.scrollerRef).to.eql('object');
						expect(
							data.scrollerRef.className.search(
								'slds-table_header-fixed_scroller'
							) >= 0
						).to.eql(true);
					}}
					selectRows="checkbox"
				>
					<DataTableColumn
						isSorted
						label="Name"
						primaryColumn
						property="name"
						sortable
						sortDirection="asc"
					/>
					<DataTableColumn label="Count" property="count" />
					<DataTableRowActions
						options={[
							{
								id: 0,
								label: 'Add to Group',
								value: '1',
							},
							{
								id: 1,
								label: 'Publish',
								value: '2',
							},
						]}
						onAction={() => {}}
						dropdown={<Dropdown length="5" />}
					/>
				</DataTable>
			).call(this);

			expect(
				this.dom.querySelectorAll('.slds-table_header-fixed_container').length
			).to.eql(1);
			expect(
				this.dom.querySelectorAll('.slds-table_header-fixed_scroller').length
			).to.eql(1);
			expect(
				this.dom
					.querySelector('table.slds-table')
					.className.search('slds-table_header-fixed') >= 0
			).to.eql(true);
			expect(this.dom.querySelectorAll('thead .slds-cell-fixed').length).to.eql(
				4
			);
		});
	});
});
