import React, {useContext} from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import {globalConfig} from '@airtable/blocks';
import {colors, colorUtils} from '@airtable/blocks/ui';

import {DragContext} from './DragWrapper';
import {
    ROW_HEIGHT,
    TEXT_PADDING_X,
    ROW_WIDTH,
    TABLE_HEADER_PATH,
    NODE_PROP_TYPE,
    COORDS_PROP_TYPE,
    TABLE_CONFIG_PROP_TYPE,
    TABLE_BORDER_WIDTH,
} from './constants';
import {calculateTableBackgroundPath} from './coordinateHelpers';

const ALLOWED_COLORS = [
    colors.BLUE_BRIGHT,
    colors.CYAN_BRIGHT,
    colors.GREEN_BRIGHT,
    colors.YELLOW_BRIGHT,
    colors.ORANGE_BRIGHT,
    colors.RED_BRIGHT,
    colors.PINK_BRIGHT,
    colors.PURPLE_BRIGHT,
    colors.GRAY_BRIGHT,
    colors.GRAY_DARK_1,
];

/**
 * Table SVG component.
 *
 * Contains a table header row (with name of table), and a field row for each field in the table.
 * Positions are calculated using offsets and ROW_WIDTH / ROW_HEIGHT constants.
 *
 * @param {Object} coords x,y coordinates for this table
 * @param {Object} tableConfig table configuration, containing table header and field nodes
 */
export default function SvgTable({coords, tableConfig}) {
    const {tableId} = tableConfig.tableNode;
    const {x, y} = coords;
    const {handleTableDrag} = useContext(DragContext);
    const canDrag = globalConfig.hasPermissionToSet();

    const tableHeight = ROW_HEIGHT * (tableConfig.fieldNodes.length + 1) + 2 * TABLE_BORDER_WIDTH;
    return (
        <svg
            stroke="black"
            x={x}
            y={y}
            width={ROW_WIDTH + 2 * TABLE_BORDER_WIDTH}
            height={ROW_HEIGHT * (tableConfig.fieldNodes.length + 1) + 2 * TABLE_BORDER_WIDTH}
        >
            <path className="TableBorder" d={calculateTableBackgroundPath(tableHeight)} />
            <TableRow
                isHeader={true}
                rowIndex={0}
                node={tableConfig.tableNode}
                onTableRowDrag={e => handleTableDrag(e, tableId)}
                canDrag={canDrag}
            />
            {tableConfig.fieldNodes.map((fieldNode, index) => {
                return (
                    <TableRow
                        key={fieldNode.id}
                        isHeader={false}
                        rowIndex={index + 1}
                        node={fieldNode}
                        onTableRowDrag={() => {}}
                        canDrag={false}
                    />
                );
            })}
        </svg>
    );
}

SvgTable.propTypes = {
    coords: COORDS_PROP_TYPE.isRequired,
    tableConfig: TABLE_CONFIG_PROP_TYPE.isRequired,
};

/**
 * Utility function to truncate text to fit in a certain width. SVG does not support overflow
 * controls for text.
 *
 * @param {string} text Text to truncate
 * @param {boolean} isHeader Whether this text is header text (which uses a larger font weight)
 * @param {number} width Allowed width
 * @returns {string}
 */
function truncateTextForWidth(text, isHeader, width = ROW_WIDTH - 2 * TEXT_PADDING_X) {
    const span = document.createElement('span');
    document.body.append(span);
    span.classList.add('TableRow', isHeader ? 'TableHeader' : undefined);
    span.innerHTML = text;

    let truncatedText = text;
    while (span.offsetWidth > width) {
        truncatedText = truncatedText.substring(0, truncatedText.length - 4) + '...';
        span.innerHTML = truncatedText;
    }

    span.remove();
    return truncatedText;
}

/**
 * Table row component, which contains either the table name or a field name.
 *
 * @param {number} rowIndex Used as a multiplier to position the field vertically
 * @param {Object} node Node object containing name and relevant ids
 * @param {boolean} isHeader Whether this table row is a the table header
 * @param {onTableRowDrag} function mousedown event handler to control table dragging
 * @param {boolean} canDrag should be true when onTableRowDrag is not a no-op
 */
function TableRow({rowIndex, node, isHeader, onTableRowDrag, canDrag}) {
    const truncatedRowName = truncateTextForWidth(node.name, isHeader);
    // Give each table header a random, deterministic color based off the tableId
    let headerColorString;
    if (isHeader) {
        const colorIndex = node.tableId.charCodeAt(node.tableId.length - 1) % ALLOWED_COLORS.length;
        headerColorString = ALLOWED_COLORS[colorIndex];
    }
    return (
        <svg
            className={classnames('TableRow', {
                TableHeader: isHeader,
                draggable: canDrag,
            })}
            id={node.id}
            x={TABLE_BORDER_WIDTH} // give room for filter box-shadow
            y={TABLE_BORDER_WIDTH + ROW_HEIGHT * rowIndex}
            onMouseDown={onTableRowDrag}
        >
            {isHeader ? (
                <path fill={colorUtils.getHexForColor(headerColorString)} d={TABLE_HEADER_PATH} />
            ) : (
                <rect height={ROW_HEIGHT} width={ROW_WIDTH} />
            )}
            <text x={TEXT_PADDING_X} y={ROW_HEIGHT / 2} width={ROW_WIDTH}>
                {truncatedRowName}
            </text>
        </svg>
    );
}

TableRow.propTypes = {
    rowIndex: PropTypes.number.isRequired,
    node: NODE_PROP_TYPE.isRequired,
    isHeader: PropTypes.bool,
    onTableRowDrag: PropTypes.func.isRequired,
};