import React, { forwardRef, useContext, HTMLAttributes } from "react";
import classNames from "classnames";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { library, FlipProp } from "@fortawesome/fontawesome-svg-core";

// far
import { faFileAlt } from "@fortawesome/free-regular-svg-icons/faFileAlt";
import { faQuestionCircle as faQuestionCircleRegular } from "@fortawesome/free-regular-svg-icons/faQuestionCircle";
import { faUser as faUserRegular } from "@fortawesome/free-regular-svg-icons/faUser";
import { faCheckCircle as faCheckCircleRegular } from "@fortawesome/free-regular-svg-icons/faCheckCircle";
import { faTimesCircle } from "@fortawesome/free-regular-svg-icons/faTimesCircle";
import { faClock } from "@fortawesome/free-regular-svg-icons/faClock";
import { faKeyboard } from "@fortawesome/free-regular-svg-icons/faKeyboard";
import { faStar } from "@fortawesome/free-regular-svg-icons/faStar";
import { faThumbsDown } from "@fortawesome/free-regular-svg-icons/faThumbsDown";
import { faThumbsUp } from "@fortawesome/free-regular-svg-icons/faThumbsUp";

// fas
import { faArchive } from "@fortawesome/free-solid-svg-icons/faArchive";
import { faArrowDown } from "@fortawesome/free-solid-svg-icons/faArrowDown";
import { faArrowLeft } from "@fortawesome/free-solid-svg-icons/faArrowLeft";
import { faArrowRight } from "@fortawesome/free-solid-svg-icons/faArrowRight";
import { faArrowUp } from "@fortawesome/free-solid-svg-icons/faArrowUp";
import { faBackward } from "@fortawesome/free-solid-svg-icons/faBackward";
import { faBan } from "@fortawesome/free-solid-svg-icons/faBan";
import { faBolt } from "@fortawesome/free-solid-svg-icons/faBolt";
import { faCalculator } from "@fortawesome/free-solid-svg-icons/faCalculator";
import { faCalendar } from "@fortawesome/free-solid-svg-icons/faCalendar";
import { faCaretDown } from "@fortawesome/free-solid-svg-icons/faCaretDown";
import { faCaretUp } from "@fortawesome/free-solid-svg-icons/faCaretUp";
import { faCheck } from "@fortawesome/free-solid-svg-icons/faCheck";
import { faCheckCircle } from "@fortawesome/free-solid-svg-icons/faCheckCircle";
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft";
import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight";
import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
import { faCircle } from "@fortawesome/free-solid-svg-icons/faCircle";
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
import { faClipboard } from "@fortawesome/free-solid-svg-icons/faClipboard";
import { faCog } from "@fortawesome/free-solid-svg-icons/faCog";
import { faColumns } from "@fortawesome/free-solid-svg-icons/faColumns";
import { faCommentAlt } from "@fortawesome/free-solid-svg-icons/faCommentAlt";
import { faCompress } from "@fortawesome/free-solid-svg-icons/faCompress";
import { faEllipsisH } from "@fortawesome/free-solid-svg-icons/faEllipsisH";
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons/faEllipsisV";
import { faEnvelope } from "@fortawesome/free-solid-svg-icons/faEnvelope";
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons/faExclamationTriangle";
import { faExpand } from "@fortawesome/free-solid-svg-icons/faExpand";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalLinkAlt";
import { faExternalLinkSquareAlt } from "@fortawesome/free-solid-svg-icons/faExternalLinkSquareAlt";
import { faEye } from "@fortawesome/free-solid-svg-icons/faEye";
import { faEyeSlash } from "@fortawesome/free-solid-svg-icons/faEyeSlash";
import { faFastBackward } from "@fortawesome/free-solid-svg-icons/faFastBackward";
import { faFastForward } from "@fortawesome/free-solid-svg-icons/faFastForward";
import { faFileAlt as faFileAltSolid } from "@fortawesome/free-solid-svg-icons/faFileAlt";
import { faFileImport } from "@fortawesome/free-solid-svg-icons/faFileImport";
import { faFileSignature } from "@fortawesome/free-solid-svg-icons/faFileSignature";
import { faFilter } from "@fortawesome/free-solid-svg-icons/faFilter";
import { faFlag } from "@fortawesome/free-solid-svg-icons/faFlag";
import { faFolder } from "@fortawesome/free-solid-svg-icons/faFolder";
import { faForward } from "@fortawesome/free-solid-svg-icons/faForward";
import { faGripLines } from "@fortawesome/free-solid-svg-icons/faGripLines";
import { faGripLinesVertical } from "@fortawesome/free-solid-svg-icons/faGripLinesVertical";
import { faHashtag } from "@fortawesome/free-solid-svg-icons/faHashtag";
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons/faInfoCircle";
import { faKey } from "@fortawesome/free-solid-svg-icons/faKey";
import { faLayerGroup } from "@fortawesome/free-solid-svg-icons/faLayerGroup";
import { faLevelUpAlt } from "@fortawesome/free-solid-svg-icons/faLevelUpAlt";
import { faListUl } from "@fortawesome/free-solid-svg-icons/faListUl";
import { faLock } from "@fortawesome/free-solid-svg-icons/faLock";
import { faLongArrowAltRight } from "@fortawesome/free-solid-svg-icons/faLongArrowAltRight";
import { faMagic } from "@fortawesome/free-solid-svg-icons/faMagic";
import { faMapSigns } from "@fortawesome/free-solid-svg-icons/faMapSigns";
import { faMinus } from "@fortawesome/free-solid-svg-icons/faMinus";
import { faPaperclip } from "@fortawesome/free-solid-svg-icons/faPaperclip";
import { faPause } from "@fortawesome/free-solid-svg-icons/faPause";
import { faPen } from "@fortawesome/free-solid-svg-icons/faPen";
import { faPlay } from "@fortawesome/free-solid-svg-icons/faPlay";
import { faPlus } from "@fortawesome/free-solid-svg-icons/faPlus";
import { faPlusCircle } from "@fortawesome/free-solid-svg-icons/faPlusCircle";
import { faPooStorm } from "@fortawesome/free-solid-svg-icons/faPooStorm";
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons/faQuestionCircle";
import { faScroll } from "@fortawesome/free-solid-svg-icons/faScroll";
import { faSearch } from "@fortawesome/free-solid-svg-icons/faSearch";
import { faShieldAlt } from "@fortawesome/free-solid-svg-icons/faShieldAlt";
import { faSlidersH } from "@fortawesome/free-solid-svg-icons/faSlidersH";
import { faSort } from "@fortawesome/free-solid-svg-icons/faSort";
import { faSortDown } from "@fortawesome/free-solid-svg-icons/faSortDown";
import { faSortUp } from "@fortawesome/free-solid-svg-icons/faSortUp";
import { faSpinner } from "@fortawesome/free-solid-svg-icons/faSpinner";
import { faSquare } from "@fortawesome/free-solid-svg-icons/faSquare";
import { faStar as faStarSolid } from "@fortawesome/free-solid-svg-icons/faStar";
import { faSync } from "@fortawesome/free-solid-svg-icons/faSync";
import { faTasks } from "@fortawesome/free-solid-svg-icons/faTasks";
import { faThLarge } from "@fortawesome/free-solid-svg-icons/faThLarge";
import { faTicketAlt } from "@fortawesome/free-solid-svg-icons/faTicketAlt";
import { faTimes } from "@fortawesome/free-solid-svg-icons/faTimes";
import { faTrash } from "@fortawesome/free-solid-svg-icons/faTrash";
import { faUser } from "@fortawesome/free-solid-svg-icons/faUser";
import { faUserAstronaut } from "@fortawesome/free-solid-svg-icons/faUserAstronaut";
import { faUserPlus } from "@fortawesome/free-solid-svg-icons/faUserPlus";
import { faUsers } from "@fortawesome/free-solid-svg-icons/faUsers";
import { faUserFriends } from "@fortawesome/free-solid-svg-icons/faUserFriends";

// Custom icon SVGs
import { DialogCheck, Scroll, Sparkle, TriangleDown, TriangleUp } from "./svg";

import { StatusContext } from "../../contexts/status";
import { STATUS_VARIANT } from "../../types";
import { bemHOF } from "../../utilities";
import { SvgProps } from "../Svg";

const cn = bemHOF("Icon");

library.add(
  faArchive,
  faArrowDown,
  faArrowLeft,
  faArrowRight,
  faArrowUp,
  faBackward,
  faBan,
  faBolt,
  faCalculator,
  faCalendar,
  faCaretDown,
  faCaretUp,
  faCheck,
  faCheckCircle,
  faCheckCircleRegular,
  faChevronDown,
  faChevronLeft,
  faChevronRight,
  faChevronUp,
  faCircle,
  faCircleNotch,
  faClipboard,
  faClock,
  faCog,
  faColumns,
  faCommentAlt,
  faCompress,
  faEllipsisH,
  faEllipsisV,
  faEnvelope,
  faExclamationTriangle,
  faExpand,
  faExternalLinkAlt,
  faExternalLinkSquareAlt,
  faEye,
  faEyeSlash,
  faFastBackward,
  faFastForward,
  faFileAlt,
  faFileAltSolid,
  faFileImport,
  faFileSignature,
  faFilter,
  faFlag,
  faFolder,
  faForward,
  faGripLines,
  faGripLinesVertical,
  faHashtag,
  faInfoCircle,
  faKey,
  faKeyboard,
  faLayerGroup,
  faLevelUpAlt,
  faListUl,
  faLock,
  faLongArrowAltRight,
  faMagic,
  faMapSigns,
  faMinus,
  faPaperclip,
  faPause,
  faPen,
  faPlay,
  faPlus,
  faPlusCircle,
  faPooStorm,
  faQuestionCircle,
  faQuestionCircleRegular,
  faScroll,
  faSearch,
  faShieldAlt,
  faSlidersH,
  faSort,
  faSortDown,
  faSortUp,
  faSpinner,
  faSquare,
  faStar,
  faStarSolid,
  faSync,
  faTasks,
  faThLarge,
  faThumbsDown,
  faThumbsUp,
  faTicketAlt,
  faTimes,
  faTimesCircle,
  faTrash,
  faUser,
  faUserAstronaut,
  faUserPlus,
  faUserRegular,
  faUsers,
  faUserFriends,
);

export enum CUSTOM_ICON_TYPE {
  DIALOG_CHECK = "dialog-check",
  SCROLL = "scroll",
  SPARKLE = "sparkle",
  TRIANGLE_DOWN = "triangle-down",
  TRIANGLE_UP = "triangle-up",
}

type CustomIconPaths = Record<
  CUSTOM_ICON_TYPE,
  (props: SvgProps) => JSX.Element
>;
const CustomIcons: CustomIconPaths = {
  [CUSTOM_ICON_TYPE.DIALOG_CHECK]: DialogCheck,
  [CUSTOM_ICON_TYPE.SCROLL]: Scroll,
  [CUSTOM_ICON_TYPE.SPARKLE]: Sparkle,
  [CUSTOM_ICON_TYPE.TRIANGLE_DOWN]: TriangleDown,
  [CUSTOM_ICON_TYPE.TRIANGLE_UP]: TriangleUp,
};

export enum ICON_TYPE {
  ARCHIVE = "archive",
  ARROW_DOWN = "arrow-down",
  ARROW_LEFT = "arrow-left",
  ARROW_RIGHT = "arrow-right",
  ARROW_UP = "arrow-up",
  BACKWARD = "backward",
  BAN = "ban",
  BOLT = "bolt",
  CALENDAR = "calendar",
  CALCULATOR = "calculator",
  CARET_DOWN = "caret-down",
  CARET_UP = "caret-up",
  CHECK = "check",
  CHECK_CIRCLE = "check-circle",
  CHEVRON_DOWN = "chevron-down",
  CHEVRON_LEFT = "chevron-left",
  CHEVRON_RIGHT = "chevron-right",
  CHEVRON_UP = "chevron-up",
  CIRCLE = "circle",
  CIRCLE_NOTCH = "circle-notch",
  CLOCK = "clock",
  CLIPBOARD = "clipboard",
  COG = "cog",
  COLUMNS = "columns",
  COMMENT_ALT = "comment-alt",
  COMPRESS = "compress",
  ELLIPSIS_H = "ellipsis-h",
  ELLIPSIS_V = "ellipsis-v",
  ENVELOPE = "envelope",
  EXCLAMATION_TRIANGLE = "exclamation-triangle",
  EXPAND = "expand",
  EXTERNAL_LINK_ALT = "external-link-alt",
  EXTERNAL_LINK_SQUARE_ALT = "external-link-square-alt",
  EYE = "eye",
  EYE_SLASH = "eye-slash",
  FAST_BACKWARD = "fast-backward",
  FAST_FORWARD = "fast-forward",
  FILE_ALT = "file-alt",
  FILE_IMPORT = "file-import",
  FILE_SIGNATURE = "file-signature",
  FILTER = "filter",
  FLAG = "flag",
  FOLDER = "folder",
  FORWARD = "forward",
  GRIP_LINES = "grip-lines",
  GRIP_LINES_VERTICAL = "grip-lines-vertical",
  HASHTAG = "hashtag",
  INFO_CIRCLE = "info-circle",
  KEY = "key",
  KEYBOARD = "keyboard",
  LAYER_GROUP = "layer-group",
  LEVEL_UP_ALT = "level-up-alt",
  LIST_UL = "list-ul",
  LOCK = "lock",
  LONG_ARROW_ALT_RIGHT = "long-arrow-alt-right",
  MAGIC = "magic",
  MAP_SIGNS = "map-signs",
  MINUS = "minus",
  PAPERCLIP = "paperclip",
  PAUSE = "pause",
  PEN = "pen",
  PLAY = "play",
  PLUS = "plus",
  PLUS_CIRCLE = "plus-circle",
  POO_STORM = "poo-storm", // Use in storybook examples
  QUESTION_CIRCLE = "question-circle",
  SCROLL = "scroll",
  SEARCH = "search",
  SHIELD_ALT = "shield-alt",
  SLIDERS_H = "sliders-h",
  SORT = "sort",
  SORT_DOWN = "sort-down",
  SORT_UP = "sort-up",
  SPINNER = "spinner",
  SQUARE = "square",
  STAR = "star",
  SYNC = "sync",
  TASKS = "tasks",
  TICKET_ALT = "ticket-alt",
  TIMES = "times",
  TIMES_CIRCLE = "times-circle",
  TH_LARGE = "th-large",
  THUMBS_DOWN = "thumbs-down",
  THUMBS_UP = "thumbs-up",
  TRASH = "trash",
  USER = "user",
  USER_ASTRONAUT = "user-astronaut",
  USER_PLUS = "user-plus",
  USERS = "users",
  USER_FRIENDS = "user-friends",
}

export enum ICON_STYLE_PREFIX {
  SOLID = "fas",
  REGULAR = "far",
}

interface BaseIconProps extends HTMLAttributes<HTMLSpanElement> {
  color?: string;
  [key: string]: any; // Until comply is on typescript v4, this is needed to allow for data-testid attribute
}

export interface SharedFaIconProps extends BaseIconProps {
  spin?: boolean;
  prefix?: ICON_STYLE_PREFIX;
  flip?: FlipProp;
}

export interface FaIconProps extends SharedFaIconProps {
  icon: ICON_TYPE;
}

export interface CustomIconProps extends BaseIconProps {
  icon: CUSTOM_ICON_TYPE;
}

export type IconProps = FaIconProps | CustomIconProps;

export interface StatusIconProps extends SharedFaIconProps {
  status?: STATUS_VARIANT;
}

const getIconClasses = ({
  className,
  color,
}: Pick<BaseIconProps, "className" | "color">) =>
  classNames("inline-flex", className, color ? `text-${color}` : "");

export const Icon = forwardRef<HTMLSpanElement, IconProps>((props, ref) => {
  const isCustomIcon = Object.values(CUSTOM_ICON_TYPE).some(
    (i) => i === props.icon,
  );

  if (isCustomIcon) {
    const { style, className, color, icon, ...rest } = props as CustomIconProps;
    const CustomIcon = CustomIcons[icon];

    return (
      <span
        ref={ref}
        className={getIconClasses({
          className: classNames(cn({ m: "custom" }), className),
          color,
        })}
        style={style}
        {...rest}
      >
        <CustomIcon
          role="img"
          focusable="false"
          aria-hidden="true"
          className="svg-inline--fa fa-fw"
        />
      </span>
    );
  }

  const {
    icon,
    style,
    className,
    spin = false,
    color,
    prefix: prefixProp,
    flip,
    ...rest
  } = props as FaIconProps;

  /** If no value is provided to the prefix prop, icons will revert to
   *  default "fas" (solid) prefix unless otherwise set in this object.
   *
   *  regular === "far"
   * */
  const iconSetDefaults: { [key in ICON_TYPE]?: ICON_STYLE_PREFIX } = {
    [ICON_TYPE.FILE_ALT]: ICON_STYLE_PREFIX.REGULAR,
    [ICON_TYPE.TIMES_CIRCLE]: ICON_STYLE_PREFIX.REGULAR,
    [ICON_TYPE.CLOCK]: ICON_STYLE_PREFIX.REGULAR,
    [ICON_TYPE.KEYBOARD]: ICON_STYLE_PREFIX.REGULAR,
    [ICON_TYPE.STAR]: ICON_STYLE_PREFIX.REGULAR,
    [ICON_TYPE.THUMBS_DOWN]: ICON_STYLE_PREFIX.REGULAR,
    [ICON_TYPE.THUMBS_UP]: ICON_STYLE_PREFIX.REGULAR,
  };

  const prefix: ICON_STYLE_PREFIX =
    prefixProp || iconSetDefaults[icon] || ICON_STYLE_PREFIX.SOLID;

  const faIcon: [ICON_STYLE_PREFIX, ICON_TYPE] = [prefix, icon];

  return (
    <span
      ref={ref}
      className={getIconClasses({ className, color })}
      style={style}
      {...rest}
    >
      <FontAwesomeIcon
        className="fa-fw"
        icon={faIcon}
        spin={spin}
        flip={flip}
      />
    </span>
  );
});

export const StatusIcon = forwardRef<HTMLSpanElement, StatusIconProps>(
  ({ status: statusProp, ...rest }, ref) => {
    const context = useContext(StatusContext);

    let computedStatus = STATUS_VARIANT.DEFAULT;
    if (statusProp) {
      computedStatus = statusProp;
    } else if (context) {
      computedStatus = context;
    }

    let icon = ICON_TYPE.INFO_CIRCLE;
    switch (computedStatus) {
      case STATUS_VARIANT.SUCCESS:
        icon = ICON_TYPE.CHECK;
        break;
      case STATUS_VARIANT.WARNING:
        icon = ICON_TYPE.EXCLAMATION_TRIANGLE;
        break;
      case STATUS_VARIANT.DANGER:
        icon = ICON_TYPE.BAN;
        break;
      default:
        break;
    }

    return <Icon ref={ref} icon={icon} {...rest} />;
  },
);
