import { DeleteOutlined, PlusOutlined, MenuOutlined, UpOutlined, DownOutlined, CopyOutlined } from '@ant-design/icons';
import { IField, IFieldList, IReactFieldListProps } from '@wowpic/xform-types';
import { Button, Card, Empty, Form, Table, Tooltip } from 'antd';
import React, { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
import Field from './field';
import Switcher from './switcher';

const AddButton: FunctionComponent<any> = ({ onClick }) => (
    <Button
        className="x-card-button"
        icon={<PlusOutlined />}
        size="large"
        onClick={onClick}>
        添加
    </Button>
);

const DragHandle = SortableHandle(() => (
    <Button
        className="action drag"
        shape="circle"
        icon={<MenuOutlined />}
    />
));

const SortableTableItem = SortableElement((props: any) => (
    <tr {...props} />
));

const SortableTableBody = SortableContainer((props: any) => (
    <tbody {...props} />
));

const SortableCardItem = SortableElement((props: any) => {
    const { item, showDelete, showSort, label, num, deleteItem, copyItem, core, cardTitleKey, collapse: _collapse } = props;

    const [collapse, setCollapse] = useState<boolean>(_collapse || false);
    const [title, setTitle] = useState(`${label || '卡片'}${num + 1}` || '');

    useEffect(() => {
        if (item?.fieldMap?.[cardTitleKey]) {
            setTitle(item?.fieldMap?.[cardTitleKey].value || `${label || '卡片'}${num + 1}` || '');
        }

        const onChange = (_: any, field: any) => {
            setTitle(field.value || `${label || '卡片'}${num + 1}` || '');
        };

        item?.fieldMap?.[cardTitleKey]?.on("VALUE_CHANGE", onChange);

        return () => {
            item?.fieldMap?.[cardTitleKey]?.removeListener("VALUE_CHANGE", onChange);
        };
    }, [cardTitleKey, num, label, item]);

    return (
        <Card
            className={`x-card ${collapse ? 'collapse' : ''}`}
            title={(
                <div className="x-card-title">
                    <div>
                        {title}
                    </div>
                </div>
            )}
            extra={
                <>
                    <Button
                        className="action"
                        shape="circle"
                        icon={<CopyOutlined />}
                        onClick={() => copyItem(num)} />
                    {
                        showDelete ? (
                            <Button
                                className="action"
                                shape="circle"
                                icon={<DeleteOutlined />}
                                onClick={() => deleteItem(num)} />
                        ) : null
                    }
                    {
                        showSort ? (
                            <DragHandle />
                        ) : null
                    }
                    <Button
                        className="action"
                        shape="circle"
                        icon={collapse ? <DownOutlined /> : <UpOutlined />}
                        onClick={() => setCollapse(!collapse)} />
                </>
            }
        >
            <Switcher core={core} fields={item.fields} />
        </Card>
    );
});

const SortableCardBody = SortableContainer((props: any) => (
    <Form.Item {...props} />
));

const FieldList: FunctionComponent<IReactFieldListProps> = ({
    core,
    field
}) => {
    const [state, setState] = useState<IFieldList>(field);
    const tableRef = useRef(null);
    const cardRef = useRef(null);
    const { status, titles, children, valid, validStatus, message, props } =
        state || {};
    let { ui } = state || {};
    const { max, min, labelCol, wrapperCol, collapse, cardTitleKey } = props;
    const { label } = ui;
    const showAdd = (!max || children.length < max) && status === 'edit';
    const showDelete = (!min || children.length > min) && status === 'edit';
    const showSort = children.length > 1 && status === 'edit';

    const addItem = useCallback(() => {
        if (status === 'edit' || !status) {
            field.add();
        }
    }, [field, status]);

    const deleteItem = useCallback(
        index => {
            field.delete(index);
        },
        [field]
    );

    const moveItem = useCallback(
        (fromIndex, toIndex) => {
            field.move(fromIndex, toIndex);
        },
        [field]
    );

    const copyItem = useCallback(
        index => {
            field.copy(index);
        },
        [field]
    );

    // 实际上 field 地址不会发生改变，即使从返回值里拿也没用，所以手动生成一个新对象来进行更新
    const setField = useCallback(() => setState({ ...field }), [field]);

    // 因为 field 地址不会改变，变的只是 state，所以以下事件监听只会触发一次
    useEffect(() => {
        field.on('CHILDREN_CHANGE', setField);
        field.on('VALID_CHANGE', setField);
        field.on('FIELD_CHANGE', setField);

        return () => {
            field.removeListener('CHILDREN_CHANGE', setField);
            field.removeListener('VALID_CHANGE', setField);
            field.removeListener('FIELD_CHANGE', setField);
        };
    }, [field, setField]);

    const addTdStyles = (node: HTMLElement) => {
        const helper = tableRef.current?.querySelector('.helper');
        if (helper) {
            const tds = node.querySelectorAll('td');
            requestAnimationFrame(() => {
                helper.querySelectorAll('td').forEach((td: HTMLElement, index: number) => {
                    if (tds[index]) {
                        td.style.width = getComputedStyle(tds[index]).width;
                    }
                });
            });
        }
    }

    if (status === 'hidden') {
        return null;
    }

    // 展示错误信息
    if (!valid) {
        ui = {
            ...ui,
            validateStatus: validStatus,
            help: message
        };
    }

    const columns: Array<any> = [
        ...titles.map(({ key, title, columnProps }: any) => ({
            key,
            title,
            dataIndex: key,
            align: 'center',
            ...(title ? { title: <Tooltip title={`${title}(${key})`} placement="left">{title}</Tooltip> } : null),
            ...columnProps
        })),
        {
            key: 'action',
            title: '操作',
            align: 'center',
            render: (_: any, __: any, index: number) => (
                <div className="actions">
                    <Button
                        className="action"
                        shape="circle"
                        icon={<CopyOutlined />}
                        onClick={() => copyItem(index)} />
                    {
                        showDelete ? (
                            <Button
                                className="action"
                                shape="circle"
                                icon={<DeleteOutlined />}
                                onClick={() => deleteItem(index)} />
                        ) : null
                    }
                    {
                        showSort ? (
                            <DragHandle />
                        ) : null
                    }
                </div>
            )
        }
    ].filter(v => v);

    const dataSource = children.map((item: any) =>
        item.fields.reduce(
            (prev: IField, fieldItem: IField) => ({
                ...prev,
                [fieldItem.key]: (
                    <Field
                        key={fieldItem.key}
                        core={item}
                        field={Object.assign(fieldItem, {
                            ui:
                                props && props.type === 'Card'
                                    ? {
                                        labelCol: labelCol || { span: 4 },
                                        wrapperCol: wrapperCol || {
                                            span: 18
                                        },
                                        ...fieldItem.ui
                                    }
                                    : { ...fieldItem.ui, label: '' }
                        })} />
                )
            }),
            { $$key: item.key }
        )
    );

    if (props && props.type === 'Card') {
        if (children.length > 0) {
            return (
                <SortableCardBody
                    {...ui}
                    {...(ui?.label ? { label: <Tooltip title={`${ui.label}(${field.key})`} placement="left">{ui.label}</Tooltip> } : null)}
                    useDragHandle
                    lockAxis="y"
                    helperContainer={() => cardRef.current}
                    onSortEnd={({ oldIndex, newIndex }: any) => {
                        moveItem(oldIndex, newIndex);
                    }}
                >
                    <div ref={cardRef}>
                        {children.map((item: any, index: number) => (
                            <SortableCardItem key={item.key} index={index} item={item} num={index} cardTitleKey={cardTitleKey} collapse={collapse} label={label} deleteItem={deleteItem} copyItem={copyItem} showDelete={showDelete} showSort={showSort} core={core} />
                        ))}
                    </div>

                    {showAdd ? <AddButton onClick={addItem} /> : null}
                </SortableCardBody>
            );
        }

        return (
            <Form.Item {...ui} {...(ui?.label ? { label: <Tooltip title={`${ui.label}(${field.key})`} placement="left">{ui.label}</Tooltip> } : null)}>
                <Card
                    className="x-card"
                    title={(
                        <div className="x-card-title">
                            <div>{label || '卡片'}</div>
                        </div>
                    )}>
                    <div className="x-card-empty" onClick={addItem}>
                        <Empty
                            {...(status === 'edit'
                                ? {
                                    description: (
                                        <span className="x-card-add">
                                            <PlusOutlined />
                                            <span>添加</span>
                                        </span>
                                    )
                                }
                                : {})} />
                    </div>
                </Card>
            </Form.Item>
        );
    }

    return (
        <Form.Item className="x-table-wrapper" {...ui} {...(ui?.label ? { label: <Tooltip title={`${ui.label}(${field.key})`} placement="left">{ui.label}</Tooltip> } : null)}>
            <div ref={tableRef}>
                <Table
                    className="x-table"
                    bordered
                    rowKey="$$key"
                    pagination={false}
                    columns={columns}
                    dataSource={dataSource}
                    components={{
                        body: {
                            wrapper: (props: any) => (
                                <SortableTableBody
                                    useDragHandle
                                    lockAxis="y"
                                    helperClass="helper"
                                    helperContainer={() => tableRef.current?.querySelector('tbody')}
                                    onSortStart={({ node }: any) => {
                                        addTdStyles(node);
                                    }}
                                    onSortEnd={({ oldIndex, newIndex }: any) => {
                                        moveItem(oldIndex, newIndex);
                                    }}
                                    {...props}
                                />
                            ),
                            row: ({ className, style, ...props }: any) => {
                                const index = children.findIndex(item => item.key === props['data-row-key']);
                                return <SortableTableItem index={index} {...props} />;
                            }
                        }
                    }}
                    {...(showAdd
                        ? {
                            footer: () => (
                                <div className="x-table-footer" onClick={addItem}>
                                    <PlusOutlined />
                                    <span>添加</span>
                                </div>
                            )
                        }
                        : {})}
                    {...props} />
            </div>
        </Form.Item>
    );
};

export default FieldList;
