import {
  DesktopWindows as DesktopWindowsIcon,
  PowerSettingsNewOutlined as PowerSettingsNewOutlinedIcon,
} from '@mui/icons-material';
import {
  Box,
  IconButton as MUIIconButton,
  IconButtonProps as MUIIconButtonProps,
} from '@mui/material';
import { FC, ReactNode, useEffect, useMemo, useState } from 'react';

import {
  BORDER_RADIUS,
  GREY,
  UNSELECTED,
} from '../../lib/consts/DEFAULT_THEME';

import api from '../../lib/api';
import FlexBox from '../FlexBox';
import IconButton, { IconButtonProps } from '../IconButton';
import { InnerPanel, InnerPanelHeader, Panel, PanelHeader } from '../Panels';
import Spinner from '../Spinner';
import { BodyText, HeaderText } from '../Text';
import { elapsed, last, now } from '../../lib/time';

type PreviewOptionalProps = {
  externalPreview?: string;
  externalTimestamp?: number;
  headerEndAdornment?: ReactNode;
  isExternalLoading?: boolean;
  isExternalPreviewStale?: boolean;
  isFetchPreview?: boolean;
  isShowControls?: boolean;
  isUseInnerPanel?: boolean;
  onClickConnectButton?: IconButtonProps['onClick'];
  onClickPreview?: MUIIconButtonProps['onClick'];
  serverName?: string;
  serverState?: string;
};

type PreviewProps = PreviewOptionalProps & {
  serverUUID: string;
};

const PREVIEW_DEFAULT_PROPS: Required<
  Omit<PreviewOptionalProps, 'onClickConnectButton' | 'onClickPreview'>
> &
  Pick<PreviewOptionalProps, 'onClickConnectButton' | 'onClickPreview'> = {
  externalPreview: '',
  externalTimestamp: 0,
  headerEndAdornment: null,
  isExternalLoading: false,
  isExternalPreviewStale: false,
  isFetchPreview: true,
  isShowControls: true,
  isUseInnerPanel: false,
  onClickConnectButton: undefined,
  onClickPreview: undefined,
  serverName: '',
  serverState: '',
};

const PreviewPanel: FC<{ isUseInnerPanel: boolean }> = ({
  children,
  isUseInnerPanel,
}) =>
  isUseInnerPanel ? (
    <InnerPanel>{children}</InnerPanel>
  ) : (
    <Panel>{children}</Panel>
  );

const PreviewPanelHeader: FC<{
  isUseInnerPanel: boolean;
  text: string | undefined;
}> = ({ children, isUseInnerPanel, text }) =>
  isUseInnerPanel ? (
    <InnerPanelHeader>
      {text ? <BodyText text={text} /> : <></>}
      {children}
    </InnerPanelHeader>
  ) : (
    <PanelHeader>
      {text ? <HeaderText text={text} /> : <></>}
      {children}
    </PanelHeader>
  );

const Preview: FC<PreviewProps> = ({
  externalPreview = PREVIEW_DEFAULT_PROPS.externalPreview,
  externalTimestamp = PREVIEW_DEFAULT_PROPS.externalTimestamp,
  headerEndAdornment,
  isExternalLoading = PREVIEW_DEFAULT_PROPS.isExternalLoading,
  isExternalPreviewStale = PREVIEW_DEFAULT_PROPS.isExternalPreviewStale,
  isFetchPreview = PREVIEW_DEFAULT_PROPS.isFetchPreview,
  isShowControls = PREVIEW_DEFAULT_PROPS.isShowControls,
  isUseInnerPanel = PREVIEW_DEFAULT_PROPS.isUseInnerPanel,
  onClickPreview: previewClickHandler,
  serverName,
  serverState = PREVIEW_DEFAULT_PROPS.serverState,
  serverUUID,
  onClickConnectButton: connectButtonClickHandle = previewClickHandler,
}) => {
  const [isPreviewLoading, setIsPreviewLoading] = useState<boolean>(true);
  const [isPreviewStale, setIsPreviewStale] = useState<boolean>(false);
  const [preview, setPreview] = useState<string>('');
  const [previewTimstamp, setPreviewTimestamp] = useState<number>(0);

  const nao = now();

  const previewButtonContent = useMemo(
    () =>
      serverState === 'running' ? (
        <>
          <Box
            alt=""
            component="img"
            src={`data:image;base64,${preview}`}
            sx={{
              height: '100%',
              opacity: isPreviewStale ? '0.4' : '1',
              padding: isUseInnerPanel ? '.2em' : 0,
              width: '100%',
            }}
          />
          {isPreviewStale &&
            ((sst: number) => {
              const { unit, value } = elapsed(nao - sst);

              return (
                <BodyText position="absolute">
                  Updated ~{value} {unit} ago
                </BodyText>
              );
            })(previewTimstamp)}
        </>
      ) : (
        <PowerSettingsNewOutlinedIcon
          sx={{
            color: UNSELECTED,
            height: '80%',
            width: '80%',
          }}
        />
      ),
    [
      isPreviewStale,
      isUseInnerPanel,
      nao,
      preview,
      previewTimstamp,
      serverState,
    ],
  );

  useEffect(() => {
    if (isFetchPreview) {
      (async () => {
        try {
          const { data } = await api.get<{
            screenshot: string;
            timestamp: number;
          }>(`/server/${serverUUID}?ss=1`);

          const { screenshot, timestamp } = data;

          setPreview(screenshot);
          setPreviewTimestamp(timestamp);
          setIsPreviewStale(!last(timestamp, 300));
        } catch {
          setIsPreviewStale(true);
        } finally {
          setIsPreviewLoading(false);
        }
      })();
    } else if (!isExternalLoading) {
      setPreview(externalPreview);
      setPreviewTimestamp(externalTimestamp);
      setIsPreviewStale(isExternalPreviewStale);
      setIsPreviewLoading(false);
    }
  }, [
    externalPreview,
    externalTimestamp,
    isExternalLoading,
    isExternalPreviewStale,
    isFetchPreview,
    serverUUID,
  ]);

  return (
    <PreviewPanel isUseInnerPanel={isUseInnerPanel}>
      <PreviewPanelHeader isUseInnerPanel={isUseInnerPanel} text={serverName}>
        {headerEndAdornment}
      </PreviewPanelHeader>
      <FlexBox row sx={{ '& > :first-child': { flexGrow: 1 } }}>
        {/* Box wrapper below is required to keep external preview size sane. */}
        <Box textAlign="center">
          {isPreviewLoading ? (
            <Spinner mt="1em" mb="1em" />
          ) : (
            <MUIIconButton
              component="span"
              disabled={!preview}
              onClick={previewClickHandler}
              sx={{
                borderRadius: BORDER_RADIUS,
                color: GREY,
                padding: 0,
              }}
            >
              {previewButtonContent}
            </MUIIconButton>
          )}
        </Box>
        {isShowControls && preview && (
          <FlexBox>
            <IconButton onClick={connectButtonClickHandle}>
              <DesktopWindowsIcon />
            </IconButton>
          </FlexBox>
        )}
      </FlexBox>
    </PreviewPanel>
  );
};

Preview.defaultProps = PREVIEW_DEFAULT_PROPS;

export default Preview;
