import { Trans, t } from '@lingui/macro';
import {
  AspectRatio,
  Button,
  Group,
  Image,
  Overlay,
  Paper,
  Text,
  rem,
  useMantineColorScheme
} from '@mantine/core';
import { Dropzone, FileWithPath, IMAGE_MIME_TYPE } from '@mantine/dropzone';
import { useHover } from '@mantine/hooks';
import { modals } from '@mantine/modals';
import { useMemo, useState } from 'react';

import { api } from '../../App';
import { UserRoles } from '../../enums/Roles';
import { cancelEvent } from '../../functions/events';
import { InvenTreeIcon } from '../../functions/icons';
import { useUserState } from '../../states/UserState';
import { PartThumbTable } from '../../tables/part/PartThumbTable';
import { vars } from '../../theme';
import { ActionButton } from '../buttons/ActionButton';
import { ApiImage } from '../images/ApiImage';
import { StylishText } from '../items/StylishText';

/**
 * Props for detail image
 */
export type DetailImageProps = {
  appRole: UserRoles;
  src: string;
  apiPath: string;
  refresh?: () => void;
  imageActions?: DetailImageButtonProps;
  pk: string;
};

/**
 * Actions for Detail Images.
 * If true, the button type will be visible
 * @param {boolean} selectExisting - PART ONLY. Allows selecting existing images as part image
 * @param {boolean} uploadFile - Allows uploading a new image
 * @param {boolean} deleteFile - Allows deleting the current image
 */
export type DetailImageButtonProps = {
  selectExisting?: boolean;
  uploadFile?: boolean;
  deleteFile?: boolean;
};

// Image is expected to be 1:1 square, so only 1 dimension is needed
const IMAGE_DIMENSION = 256;

// Image to display if instance has no image
const backup_image = '/static/img/blank_image.png';

/**
 * Modal used for removing/deleting the current image relation
 */
const removeModal = (apiPath: string, setImage: (image: string) => void) =>
  modals.openConfirmModal({
    title: <StylishText size="xl">{t`Remove Image`}</StylishText>,
    children: (
      <Text>
        <Trans>Remove the associated image from this item?</Trans>
      </Text>
    ),
    labels: { confirm: t`Remove`, cancel: t`Cancel` },
    onConfirm: async () => {
      await api.patch(apiPath, { image: null });
      setImage(backup_image);
    }
  });

/**
 * Modal used for uploading a new image
 */
function UploadModal({
  apiPath,
  setImage
}: {
  apiPath: string;
  setImage: (image: string) => void;
}) {
  const [currentFile, setCurrentFile] = useState<FileWithPath | null>(null);
  let uploading = false;

  // Components to show in the Dropzone when no file is selected
  const noFileIdle = (
    <Group>
      <InvenTreeIcon icon="photo" iconProps={{ size: '3.2rem', stroke: 1.5 }} />
      <div>
        <Text size="xl" inline>
          <Trans>Drag and drop to upload</Trans>
        </Text>
        <Text size="sm" color="dimmed" inline mt={7}>
          <Trans>Click to select file(s)</Trans>
        </Text>
      </div>
    </Group>
  );

  /**
   * Generates components to display selected image in Dropzone
   */
  const fileInfo = (file: FileWithPath) => {
    const imageUrl = URL.createObjectURL(file);
    const size = file.size / 1024 ** 2;

    return (
      <div
        style={{
          display: 'flex',
          flexDirection: 'row',
          gap: '15px',
          flexGrow: '1'
        }}
      >
        <Image
          src={imageUrl}
          onLoad={() => URL.revokeObjectURL(imageUrl)}
          radius="sm"
          height={75}
          fit="contain"
          style={{ flexBasis: '40%' }}
        />
        <div style={{ flexBasis: '60%' }}>
          <Text size="xl" inline style={{ wordBreak: 'break-all' }}>
            {file.name}
          </Text>
          <Text size="sm" color="dimmed" inline mt={7}>
            {size.toFixed(2)} MB
          </Text>
        </div>
      </div>
    );
  };

  /**
   * Create FormData object and upload selected image
   */
  const uploadImage = async (file: FileWithPath | null) => {
    if (!file) {
      return;
    }

    uploading = true;
    const formData = new FormData();
    formData.append('image', file, file.name);

    const response = await api.patch(apiPath, formData);

    if (response.data.image.includes(file.name)) {
      setImage(response.data.image);
      modals.closeAll();
    }
  };

  const { colorScheme } = useMantineColorScheme();

  const primaryColor =
    vars.colors.primaryColors[colorScheme === 'dark' ? 4 : 6];
  const redColor = vars.colors.red[colorScheme === 'dark' ? 4 : 6];

  return (
    <Paper style={{ height: '220px' }}>
      <Dropzone
        onDrop={(files) => setCurrentFile(files[0])}
        maxFiles={1}
        accept={IMAGE_MIME_TYPE}
        loading={uploading}
      >
        <Group
          justify="center"
          gap="xl"
          style={{ minHeight: rem(140), pointerEvents: 'none' }}
        >
          <Dropzone.Accept>
            <InvenTreeIcon
              icon="upload"
              iconProps={{
                size: '3.2rem',
                stroke: 1.5,
                color: primaryColor
              }}
            />
          </Dropzone.Accept>
          <Dropzone.Reject>
            <InvenTreeIcon
              icon="reject"
              iconProps={{
                size: '3.2rem',
                stroke: 1.5,
                color: redColor
              }}
            />
          </Dropzone.Reject>
          <Dropzone.Idle>
            {currentFile ? fileInfo(currentFile) : noFileIdle}
          </Dropzone.Idle>
        </Group>
      </Dropzone>
      <Paper
        style={{
          position: 'sticky',
          bottom: '0',
          left: '0',
          right: '0',
          height: '60px',
          zIndex: 1,
          display: 'flex',
          alignItems: 'center',
          flexDirection: 'row',
          justifyContent: 'flex-end',
          gap: '10px'
        }}
      >
        <Button
          variant="outline"
          disabled={!currentFile}
          onClick={() => setCurrentFile(null)}
        >
          <Trans>Clear</Trans>
        </Button>
        <Button
          disabled={!currentFile}
          onClick={() => uploadImage(currentFile)}
        >
          <Trans>Submit</Trans>
        </Button>
      </Paper>
    </Paper>
  );
}

/**
 * Generate components for Action buttons used with the Details Image
 */
function ImageActionButtons({
  actions = {},
  visible,
  apiPath,
  hasImage,
  pk,
  setImage
}: {
  actions?: DetailImageButtonProps;
  visible: boolean;
  apiPath: string;
  hasImage: boolean;
  pk: string;
  setImage: (image: string) => void;
}) {
  return (
    <>
      {visible && (
        <Group
          gap="xs"
          style={{ zIndex: 2, position: 'absolute', top: '10px', left: '10px' }}
        >
          {actions.selectExisting && (
            <ActionButton
              icon={
                <InvenTreeIcon
                  icon="select_image"
                  iconProps={{ color: 'white' }}
                />
              }
              tooltip={t`Select from existing images`}
              variant="outline"
              size="lg"
              tooltipAlignment="top"
              onClick={(event: any) => {
                cancelEvent(event);

                modals.open({
                  title: <StylishText size="xl">{t`Select Image`}</StylishText>,
                  size: 'xxl',
                  children: <PartThumbTable pk={pk} setImage={setImage} />
                });
              }}
            />
          )}
          {actions.uploadFile && (
            <ActionButton
              icon={
                <InvenTreeIcon icon="upload" iconProps={{ color: 'white' }} />
              }
              tooltip={t`Upload new image`}
              variant="outline"
              size="lg"
              tooltipAlignment="top"
              onClick={(event: any) => {
                cancelEvent(event);
                modals.open({
                  title: <StylishText size="xl">{t`Upload Image`}</StylishText>,
                  children: (
                    <UploadModal apiPath={apiPath} setImage={setImage} />
                  )
                });
              }}
            />
          )}
          {actions.deleteFile && hasImage && (
            <ActionButton
              icon={
                <InvenTreeIcon icon="delete" iconProps={{ color: 'red' }} />
              }
              tooltip={t`Delete image`}
              variant="outline"
              size="lg"
              tooltipAlignment="top"
              onClick={(event: any) => {
                cancelEvent(event);
                removeModal(apiPath, setImage);
              }}
            />
          )}
        </Group>
      )}
    </>
  );
}

/**
 * Renders an image with action buttons for display on Details panels
 */
export function DetailsImage(props: Readonly<DetailImageProps>) {
  // Displays a group of ActionButtons on hover
  const { hovered, ref } = useHover();
  const [img, setImg] = useState<string>(props.src ?? backup_image);

  // Sets a new image, and triggers upstream instance refresh
  const setAndRefresh = (image: string) => {
    setImg(image);
    props.refresh && props.refresh();
  };

  const permissions = useUserState();

  const hasOverlay: boolean = useMemo(() => {
    return (
      props.imageActions?.selectExisting ||
      props.imageActions?.uploadFile ||
      props.imageActions?.deleteFile ||
      false
    );
  }, [props.imageActions]);

  const expandImage = (event: any) => {
    cancelEvent(event);
    modals.open({
      children: <ApiImage src={img} />,
      withCloseButton: false
    });
  };

  return (
    <AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos="relative">
      <>
        <ApiImage
          src={img}
          mah={IMAGE_DIMENSION}
          maw={IMAGE_DIMENSION}
          onClick={expandImage}
        />
        {permissions.hasChangeRole(props.appRole) && hasOverlay && hovered && (
          <Overlay color="black" opacity={0.8} onClick={expandImage}>
            <ImageActionButtons
              visible={hovered}
              actions={props.imageActions}
              apiPath={props.apiPath}
              hasImage={props.src ? true : false}
              pk={props.pk}
              setImage={setAndRefresh}
            />
          </Overlay>
        )}
      </>
    </AspectRatio>
  );
}
