// Copyright 2021 Northern.tech AS
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
import type { CSSProperties, ReactNode } from 'react';
import React, { Fragment, createContext, useContext, useEffect, useRef, useState } from 'react';

// material ui
import { Chip, Typography } from '@mui/material';
import { makeStyles } from 'tss-react/mui';

import copy from 'copy-to-clipboard';

import { CopyableText } from './CopyableText';

const contentWidth = 650;

const getGridTemplateColumnSizing = (columnWidth: string): string => `${columnWidth} minmax(auto, ${contentWidth}px)`;

type DataValue = ReactNode | string;

const useStyles = makeStyles()(theme => ({
  root: {
    '&.two-columns.column-data': {
      gridTemplateColumns: getGridTemplateColumnSizing('max-content'),
      maxWidth: 'initial',
      rowGap: theme.spacing()
    }
  }
}));

const ValueColumn = ({ setSnackbar, value = '' }: { setSnackbar?: (message: string) => void; value?: DataValue }) => {
  const copyableValue = React.isValidElement(value) ? value.props.value : value;

  if (!setSnackbar) {
    return <CopyableText title={copyableValue}>{value}</CopyableText>;
  }

  const onCopy = () => {
    copy(copyableValue);
    setSnackbar('Value copied to clipboard');
  };

  return (
    <CopyableText onCopy={onCopy} textClasses="clickable" title={copyableValue}>
      {value}
    </CopyableText>
  );
};

interface KeyColumnProps {
  chipLikeKey?: boolean;
  isTooWide?: boolean;
  setColumnWidth?: (width: number) => void;
  value: string;
}

const KeyColumn = ({ chipLikeKey, isTooWide, setColumnWidth, value }: KeyColumnProps) => {
  const ref = useRef<HTMLSpanElement>(null);

  useEffect(() => {
    if (ref.current && setColumnWidth) {
      const width = ref.current.scrollWidth;
      setColumnWidth(width);
    }
  }, [setColumnWidth, value]);

  const isMeasuringTooWide = isTooWide || (ref.current && ref.current.scrollWidth > contentWidth / 3);

  return chipLikeKey ? (
    <Chip label={value} size="small" style={{ justifySelf: 'end' }} />
  ) : (
    <Typography ref={ref} className="key" variant="subtitle2" style={isMeasuringTooWide ? { wordBreak: 'break-word' } : { whiteSpace: 'nowrap' }}>
      {value}
    </Typography>
  );
};

export interface TwoColumnDataProps {
  chipLikeKey?: boolean;
  className?: string;
  columnWidth?: number;
  data?: Record<string, DataValue>;
  isTooWide?: boolean;
  setColumnWidth?: (width: number) => void;
  setSnackbar?: (message: string) => void;
  style?: CSSProperties;
}

export const TwoColumnData = ({
  chipLikeKey = false,
  className = '',
  columnWidth,
  data = {},
  isTooWide,
  setColumnWidth,
  setSnackbar,
  style = {}
}: TwoColumnDataProps) => {
  const { classes } = useStyles();
  return (
    <div
      className={`break-all two-columns ${classes.root} column-data ${className}`}
      style={{ ...style, gridTemplateColumns: columnWidth ? getGridTemplateColumnSizing(`${Math.min(contentWidth / 3, columnWidth)}px`) : undefined }}
    >
      {Object.entries(data).map(([key, value]) => (
        <Fragment key={key}>
          <KeyColumn chipLikeKey={chipLikeKey} isTooWide={isTooWide} setColumnWidth={setColumnWidth} value={key} />
          <ValueColumn setSnackbar={setSnackbar} value={value} />
        </Fragment>
      ))}
    </div>
  );
};

const ColumnWidthContext = createContext<{ columnWidth: number; isTooWide: boolean; setColumnWidth: (width: number) => void } | null>(null);

export const ColumnWidthProvider = ({ children }: { children: ReactNode }) => {
  const [columnWidth, setColumnWidth] = useState<number>(0);

  const updateColumnWidth = (width: number) => setColumnWidth(currentWidth => Math.max(currentWidth, width));

  const isTooWide = columnWidth > contentWidth / 3;

  return <ColumnWidthContext.Provider value={{ columnWidth, isTooWide, setColumnWidth: updateColumnWidth }}>{children}</ColumnWidthContext.Provider>;
};

export const SynchronizedTwoColumnData = (props: Omit<TwoColumnDataProps, 'columnWidth' | 'setColumnWidth'>) => {
  const { columnWidth, isTooWide, setColumnWidth } = useContext(ColumnWidthContext)!;
  return <TwoColumnData {...props} columnWidth={columnWidth} isTooWide={isTooWide} setColumnWidth={setColumnWidth} />;
};
