import React, { ReactNode } from 'react';
import { AudienceAge, Id, Label, MetricsMetadata, Optional, StatusId, TargetItems } from 'backend-api/models';
import { chain, isEqual, uniqueId } from 'lodash';
import { isStringArray } from 'utils/array';
import {
  ICON_COLOR_TO_COLOR,
  ID_BY_STATUS,
  INVALID_LINK_LABEL,
  MAX_AGE,
  MIN_AGE,
  SHORT_DATE_REQUEST_FORMAT,
  Status,
  STATUS_BY_ID,
  STATUS_ICONS_BY_ID,
} from 'common/constants';
import { EMPTY_MARKDOWN } from 'common/components/markdown-editor';
import { isPlaylists } from 'backend-api/guards';
import { LinkSelectOption } from 'common/components/form/links/types';
import { IconColor, IconName } from 'common/components/icon/types';
import { DecibelLink, LinkPlatform } from 'utils/decibel-link';
import { Sort, SortOrder } from 'utils/sort';
import { Tooltip } from 'common/components/tooltip';
import { MetricId } from 'common/types';
import { FromSearchParam } from 'backend-api/types';
import { ROLES } from 'common-v2/constants';
import { parseDateFromISOString, getFormattedDate, addDaysToDate } from 'common-v2/utils';

export const getDatesForState = (query, formatter: (date: unknown) => unknown) => [
  formatter(query.earliestStartDate),
  formatter(query.latestEndDate),
];

export const getNamesString = (items?: { name: string }[]): Optional<string> =>
  items?.map(item => item.name).join(', ');

export const getJoinedNames = <T extends { name?: string }>(items: T[]): string =>
  items
    .filter(({ name }) => !!name)
    .map(({ name }) => name)
    .join(', ');

export const isEmptyMarkdown = markdown => !markdown || markdown === EMPTY_MARKDOWN;

export const getAudienceString = (item?: AudienceAge): Optional<string> => {
  if (!item) return undefined;
  const lowerAge = item.lowerAge || MIN_AGE;
  const higherAge = item.higherAge || MAX_AGE;
  const normalizedLowerAge = lowerAge < MIN_AGE ? MIN_AGE : lowerAge;
  const normalizedHigherAge = higherAge > MAX_AGE ? MAX_AGE : higherAge;

  return item ? `${normalizedLowerAge}-${normalizedHigherAge}+` : undefined;
};

export const parseStringArray = (value: any): Optional<string[]> => {
  if (isStringArray(value)) return value;
  if (value === null) return [];

  return undefined;
};

export const parseStringArrayForApi = (value: any): Optional<string[]> | null => {
  if (isStringArray(value) || value === null) return value;

  return [];
};

export const filterBySearchTextInOptionLabel = (option, searchText?: string): boolean => {
  if (!searchText) return true;

  return chain(option)
    .get('label')
    .toLower()
    .includes(searchText.toLowerCase())
    .value();
};

export const getStatusIconName = (statusId: StatusId) => STATUS_ICONS_BY_ID[statusId];
export const getStatusName = (statusId: StatusId) => STATUS_BY_ID[statusId];
export const getStatusId = (statusName: Status) => ID_BY_STATUS[statusName];

export const getFlagNameByCountryName = (name: string) => {
  const fileName = name
    .toLowerCase()
    .split(' ')
    .join('-');

  return fileName;
};

export const isGrasId = (id: string): boolean => id.startsWith('GRAS_');

export const getRoleNameById = (roleId: Id): Optional<string> =>
  Object.values(ROLES).find(role => role.id === roleId)?.name;

export const getNextDayFormattedDate = (dateString: string, format: string = SHORT_DATE_REQUEST_FORMAT): string => {
  const parsedDate = parseDateFromISOString(dateString);
  const formatDate = getFormattedDate(format);
  return formatDate(addDaysToDate(parsedDate, 1));
};

export function prepareDatesForRequest(query, formatter: (date: any) => any) {
  const [earliestStartDate, latestEndDate] = query.dates.map(formatter);

  return {
    earliestStartDate,
    latestEndDate,
  };
}

export const getFieldsWithDiffValues = (firstObj?: any, secondObj?: any) => {
  if (!firstObj || !secondObj) return null;

  return Object.keys(firstObj).reduce((result, key: string) => {
    return isEqual(firstObj[key], secondObj[key]) ? result : result.concat(key);
  }, Array<any>());
};

export const getMultipleItemsString = (items: { name: string }[], formatted?: boolean): string | ReactNode => {
  const maxTitlesShown = 3;
  if (items.length === 0) return '';
  if (items.length === 1)
    return formatted ? (
      <p>
        <b>{items[0].name}</b>
      </p>
    ) : (
      items[0].name
    );

  if (items.length <= maxTitlesShown) {
    const allButLastTitles = items
      .slice(0, -1)
      .map(item => item.name)
      .join(', ');

    const lastTitle = items[items.length - 1].name;
    return formatted ? (
      <p>
        <b>{allButLastTitles}</b>
        {' and '}
        <b>{lastTitle}</b>
      </p>
    ) : (
      `${allButLastTitles} and ${lastTitle}`
    );
  }

  const firstThreeTitles = items
    .slice(0, maxTitlesShown)
    .map(item => item.name)
    .join(', ');

  const restTitlesArray = items.slice(items.length - maxTitlesShown, items.length);
  const restTitles = getNamesString(restTitlesArray);

  return formatted ? (
    <p>
      <b>{firstThreeTitles}</b>
      {' and '}
      <Tooltip content={restTitles}>
        <b>{restTitlesArray.length} more</b>
      </Tooltip>
    </p>
  ) : (
    `${firstThreeTitles} and ${restTitlesArray.length} more`
  );
};

export const getLinkLabel = (link: string): string => {
  try {
    return new DecibelLink(link).label;
  } catch (e) {
    return INVALID_LINK_LABEL;
  }
};

export const getLinksWithLabels = (links: string[], allowInvalid = false): LinkSelectOption[] => {
  return links.reduce<LinkSelectOption[]>((previousValue, currentValue, index) => {
    let link: DecibelLink;

    try {
      link = new DecibelLink(currentValue);
    } catch (error) {
      if (allowInvalid) {
        return [
          ...previousValue,
          {
            id: index,
            link: currentValue,
            label: currentValue,
            icon: 'link',
            isInvalid: true,
          },
        ];
      } else {
        throw error;
      }
    }

    const linkLabel = link.label;
    const sameTitleCount = previousValue.filter(inner => inner.label.includes(linkLabel)).length;
    const labelWithIndex = sameTitleCount > 0 ? `${linkLabel} ${sameTitleCount}` : linkLabel;

    const linkIcon = getPlatformIconName(link.platform);

    return [
      ...previousValue,
      {
        id: index,
        link: link.url.href,
        label: labelWithIndex,
        icon: linkIcon,
      },
    ];
  }, []);
};

export const getPrimaryTargets = (targets: TargetItems) =>
  isPlaylists(targets) ? targets : targets.filter(item => item.type === 'Primary');

export const sortFromFieldId = (fieldId: Optional<number | string>) =>
  new Sort(
    SortOrder.Descending,
    fieldId === undefined ? '' : typeof fieldId === 'string' ? fieldId : fieldId.toString()
  );

export const getPlatformIconName = (platform: LinkPlatform): Optional<IconName> => {
  switch (platform) {
    case LinkPlatform.Amazon:
      return 'amazon';
    case LinkPlatform.Apple:
      return 'apple';
    case LinkPlatform.Deezer:
      return 'deezer';
    case LinkPlatform.Facebook:
      return 'facebook';
    case LinkPlatform.Google:
      return 'google';
    case LinkPlatform.Instagram:
      return 'instagram';
    case LinkPlatform.Linkfire:
      return 'linkfire';
    case LinkPlatform.Pandora:
      return 'pandora';
    case LinkPlatform.Shazam:
      return 'shazam';
    case LinkPlatform.Snapchat:
      return 'snapchat';
    case LinkPlatform.SoundCloud:
      return 'soundcloud';
    case LinkPlatform.Spotify:
      return 'spotify';
    case LinkPlatform.Tidal:
      return 'tidal';
    case LinkPlatform.TikTok:
      return 'tiktok';
    case LinkPlatform.Twitter:
      return 'twitter';
    case LinkPlatform.YouTube:
      return 'youtube';
    default:
      return undefined;
  }
};

export const getUnavailableLabels = (userLabels: Label[], targetLabels: Label[]): Label[] => {
  if (targetLabels.length === 0) {
    return [];
  }

  const userLabelsIds = userLabels.map(({ id }) => id);

  return targetLabels.filter(({ id }) => !userLabelsIds.includes(id));
};

export const colorFromIconColor = (iconColor?: IconColor): string => {
  if (!iconColor) {
    return ICON_COLOR_TO_COLOR.default;
  }

  return ICON_COLOR_TO_COLOR[iconColor] || ICON_COLOR_TO_COLOR.default;
};

export const getMetricLegendItemTooltip = (metric: MetricId, metadata?: MetricsMetadata): Optional<string> => {
  if (!metadata) return;

  switch (metric) {
    case 'revenueStreams':
      return `Estimated revenue is calculated using a blended rate $${metadata.crossPlatformBlendedRate} per stream.`;
    case 'revenueSpotifyStreams':
      return `Spotify's estimated revenue is calculated using a blended rate $${metadata.spotifyBlendedRate} per stream.`;
    case 'revenueAppleStreams':
      return `Apple Music's estimated revenue is calculated using a blended rate $${metadata.appleBlendedRate} per stream.`;
    case 'revenueAmazonStreams':
      return `Amazon Music's estimated revenue is calculated using a blended rate between $${metadata.amazonBlendedRate} per stream.`;
    default:
      return;
  }
};

export const generateUniqueId = (): string => uniqueId(Date.now().toString());

export const getAuthUrlWithRedirectParam = (authUrl: string, redirectToPath?: string): string => {
  return !!redirectToPath ? authUrl + '?next=' + window.location.origin + redirectToPath : authUrl;
};

export const getFromSearchParams = (query: Record<string, unknown>): Record<'params', FromSearchParam> | {} => {
  if (query.fromSearch !== 1) return {};
  return { params: { fromSearch: 1 } };
};
