import { type FieldNamesMarkedBoolean } from 'react-hook-form';

import axios from 'axios';
import { type Theme } from '@material-hu/mui/styles';
import { type SortableAttachment } from '@material-hu/types/attachments';
import { IconType } from '@material-hu/types/icons';

import { type AudienceExpressionSearchRequest } from '@material-hu/components/composed-components/audience/types';
import { type AvatarProps } from '@material-hu/components/design-system/Avatar/types';
import { type FileCardType } from '@material-hu/components/design-system/FileCard/types';

import { queryClient } from 'src/config/react-query';
import { signedUploadFile } from 'src/pages/dashboard/serviceManagement/utils';
import { type Icon } from 'src/types/icons';
import { bytesToSize } from 'src/utils/bytes';
import { preprocessImage } from 'src/utils/files';
import { formatOutgoingIcon } from 'src/utils/icons';

import { MIN_SEARCH_LENGTH } from './constants';
import { librariesKeys } from './queries';
import { type ArticleFormValues } from './schemas';
import { searchAudienceByExpression } from './services';
import {
  type Article,
  type ArticleAttachmentPayload,
  ArticleStatus,
  type ArticleUpdatePayload,
} from './types';

/**
 * Checks if an article has children (subarticles).
 */
export const articleHasChildren = (article: Article): boolean =>
  (article.children?.length ?? 0) > 0;

export const formatArticleEditDefaults = (
  article: Article,
): ArticleFormValues => ({
  icon: article.icon,
  title: article.title,
  body: article.body ?? '',
  coverPicture: article.coverPicture
    ? { url: article.coverPicture, file: null }
    : null,
  showCoverPicture: !!article.coverPicture,
  attachments: (article.attachments ?? []) as SortableAttachment[],
  sendNotification: article.notify ?? false,
  notificationType: article.notificationType ?? null,
});

export const formatAttachmentPayload = (
  attachment: SortableAttachment,
  index: number,
): ArticleAttachmentPayload => ({
  url: attachment.url,
  name: attachment.name,
  type: attachment.type,
  size: attachment.size,
  bytes: attachment.bytes,
  id: attachment.id >= 0 ? attachment.id : undefined,
  position: index,
});

export const formatArticleEditPayload = async (
  values: ArticleFormValues,
  dirtyFields?: FieldNamesMarkedBoolean<ArticleFormValues>,
): Promise<ArticleUpdatePayload> => ({
  title: values.title,
  body: values.body,
  icon: values.icon
    ? (formatOutgoingIcon(
        values.icon as unknown as Icon,
      ) as unknown as ArticleFormValues['icon'])
    : undefined,
  coverPictureUrl:
    values.showCoverPicture && values.coverPicture
      ? await preprocessImage({
          file: values.coverPicture.file ?? null,
          url: values.coverPicture.url ?? null,
        })
      : undefined,
  removeCoverPicture: !values.showCoverPicture,
  attachments: values.attachments.map(formatAttachmentPayload),
  replaceAttachments: !!dirtyFields?.attachments,
  sendNotification: values.sendNotification,
  notificationType: values.notificationType,
});

export const getCoverPictureSrc = (
  showCoverPicture: ArticleFormValues['showCoverPicture'],
  coverPicture: ArticleFormValues['coverPicture'],
): string | null => {
  if (!showCoverPicture || !coverPicture) return null;
  if (coverPicture.file) return URL.createObjectURL(coverPicture.file);
  return coverPicture.url;
};

export const getArticleAvatarProps = (
  article: Article,
  theme: Theme,
): AvatarProps => {
  const isEditor = article.isEditor ?? false;
  const isActive = article.status === ArticleStatus.ENABLED;

  return {
    src: article.icon.type === IconType.IMAGE ? article.icon.value : undefined,
    text: article.icon.type === IconType.EMOJI ? article.icon.value : undefined,
    withBadge: isEditor,
    size: 'medium',
    sx: {
      '& .MuiAvatar-img': { width: theme.spacing(3), height: theme.spacing(3) },
      '& .MuiTypography-root': { fontSize: theme.typography.globalL.fontSize },
    },
    badgeProps: {
      variant: 'dot',
      color: isActive ? 'success' : 'disabled',
    },
  };
};

export const getCachedArticleById = (articleId: number) => {
  const key = librariesKeys.article.byId(articleId);
  const cachedArticle = queryClient.getQueryData<{ data: Article }>(key);
  return cachedArticle?.data ?? null;
};

export const uploadArticleAsset = async (
  file: File,
  options?: { signal?: AbortSignal },
): Promise<FileCardType> => {
  const { signal } = options ?? {};
  if (signal?.aborted) {
    throw new DOMException('The operation was aborted.', 'AbortError');
  }

  try {
    const fileAsset = await signedUploadFile(file, false, true, signal);
    return {
      status: 'success',
      attachment: {
        url: fileAsset.url,
        name: file.name,
        size: bytesToSize(file.size),
        bytes: file.size,
        type: file.type,
      },
    };
  } catch (error) {
    if (axios.isCancel(error)) {
      throw new DOMException('The operation was aborted.', 'AbortError');
    }
    return { status: 'error', file };
  }
};

export const isSearchValid = (value: string) =>
  value.length >= MIN_SEARCH_LENGTH;

export const invalidateSidebarArticles = (rootId?: number) => {
  queryClient.invalidateQueries(librariesKeys.article.rootList());
  if (rootId) {
    queryClient.invalidateQueries(librariesKeys.article.childrenTree(rootId));
  }
};

export const getCollaboratorsService =
  (
    segmentationExpression: AudienceExpressionSearchRequest['segmentationExpression'],
  ) =>
  (params: Pick<AudienceExpressionSearchRequest, 'limit' | 'cursor' | 'q'>) =>
    searchAudienceByExpression({ ...params, segmentationExpression });
