import type { ReactNode } from 'react';
import React, { useRef } from 'react';
import { SuiteLogger, get } from '@theorchard/suite-utils';
import isEqual from 'lodash.isequal';
import { renderSingleNode } from '../../../utils';
import { TABLE_CLASS_GRID } from './constants';
import type { ColumnInstance, RowInstance } from './types';

export const createClassName = (prefix: string, name: string) =>
    `${prefix}-${name.split('.').join('-')}`;

export function createGridStyles<Data>(
    columns: ColumnInstance<Data>[]
): Pick<HTMLElement['style'], 'gridTemplateColumns'> {
    const gridTemplateColumns = columns
        .filter((column) => column.display === 'visible')
        .reduce((result, { definition }) => {
            const gridMin = definition.minWidth || 'min-content';
            const gridMax = definition.maxWidth || '1fr';
            return `${result} minmax(${gridMin}, ${gridMax})`;
        }, '')
        .trim();

    return {
        gridTemplateColumns,
    };
}

export function getCellValue<Data>(
    row: RowInstance<Data>,
    column: ColumnInstance<Data>
): string | number | null | undefined {
    const value = get(row.data, column.definition.name);

    if (
        value === undefined ||
        value === null ||
        typeof value === 'string' ||
        typeof value === 'number'
    )
        return value;

    return String(value);
}

export function getCellStringValue<Data>(
    row: RowInstance<Data>,
    column: ColumnInstance<Data>
): string {
    const value = getCellValue(row, column);

    if (value === undefined || value === null) return '-';

    return value.toString();
}

export function useDeepMemo<TKey, TValue>(memoFn: () => TValue, key: TKey): TValue {
    const ref = useRef<{ key: TKey; value: TValue }>();

    if (!ref.current || !isEqual(key, ref.current.key)) ref.current = { key, value: memoFn() };

    return ref.current.value;
}

export const prependNode = (
    node: ReactNode | React.FC,
    source: ReactNode | undefined
): ReactNode | ReactNode[] => {
    const reactNode = renderSingleNode(node);
    if (!source) return reactNode;
    if (Array.isArray(source)) return [reactNode, ...source];
    return [reactNode, source];
};

export const renderNode = (node: ReactNode | React.FC) => {
    if (!node) return null;

    if (Array.isArray(node)) return <>{node.map(renderSingleNode)}</>;

    return renderSingleNode(node);
};

const STORAGE_KEY = '@theorchard/suite-components/gridTable';

const getStorageKey = (name: string, setting: string) => `${STORAGE_KEY}:${name}:${setting}`;

export const setTableSetting = (name: string, setting: string, data: unknown) => {
    localStorage.setItem(getStorageKey(name, setting), JSON.stringify(data));
};

export const getTableSetting = (name: string, setting: string): unknown => {
    const json = localStorage.getItem(getStorageKey(name, setting));
    if (!json) return undefined;

    try {
        return JSON.parse(json) as unknown;
    } catch {
        return undefined;
    }
};

export const missingHandler = (name: string) => () => {
    SuiteLogger.warn('GridTable', `${name}: is not handled`);
};

export const getElementHeight = (wrapper: HTMLElement, selector: string) =>
    wrapper.querySelector(selector)?.getBoundingClientRect().height ?? 0;

export const classNameToSelector = (className: string) => `.${className.split(' ').join('.')}`;

export const getInnerScrollContainer = (wrapper: HTMLElement) =>
    wrapper.querySelector(classNameToSelector(TABLE_CLASS_GRID));

export const getOuterScrollContainer = (element: Element): Element | undefined => {
    if (element.scrollHeight > element.clientHeight) return element;
    else if (element.parentElement) return getOuterScrollContainer(element.parentElement);
    return undefined;
};

export const getOuterScrollContainerPadding = (node: Element) => {
    const container = getOuterScrollContainer(node);
    if (!container) return 0;

    const { padding } = window.getComputedStyle(container);
    if (!padding) return 0;
    return parseInt(padding, 10);
};
