import React from 'react';
import type { ComponentProps, MouseEventHandler, ReactNode } from 'react';
import { NavLink, useMatch } from 'react-router';

import { Button } from '@actual-app/components/button';
import { styles } from '@actual-app/components/styles';
import type { CSSProperties } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { css } from '@emotion/css';

import { useNavigate } from '#hooks/useNavigate';

type TextLinkProps = {
  style?: CSSProperties;
  onClick?: MouseEventHandler;
  children?: ReactNode;
};

type ButtonLinkProps = Omit<
  ComponentProps<typeof Button>,
  'variant' | 'style'
> & {
  buttonVariant?: ComponentProps<typeof Button>['variant'];
  style?: CSSProperties;
  to?: string;
  activeStyle?: CSSProperties;
};

type InternalLinkProps = {
  to?: string;
  style?: CSSProperties;
  activeStyle?: CSSProperties;
  children?: ReactNode;
  isDisabled?: boolean;
  isExactPathMatch?: boolean;
};

const externalLinkColors = {
  purple: theme.pageTextPositive,
  blue: theme.pageTextLink,
  muted: 'inherit',
};

type ExternalLinkProps = {
  children?: ReactNode;
  to?: string;
  linkColor?: keyof typeof externalLinkColors;
  onClick?: MouseEventHandler;
  className?: string;
};

const ExternalLink = ({
  children,
  to,
  linkColor = 'blue',
  onClick,
  className,
}: ExternalLinkProps) => {
  return (
    // we can't use <ExternalLink /> here for obvious reasons
    <a
      href={to ?? ''}
      target="_blank"
      rel="noopener noreferrer"
      style={{ color: externalLinkColors[linkColor] }}
      className={className}
      onClick={onClick}
    >
      {children}
    </a>
  );
};

const TextLink = ({ style, onClick, children, ...props }: TextLinkProps) => {
  return (
    <Text
      style={{
        backgroundColor: 'transparent',
        display: 'inline',
        border: 0,
        cursor: 'pointer',
        textDecoration: 'underline',
        ':hover': {
          boxShadow: 'none',
        },
        ...style,
      }}
      {...props}
      onClick={onClick}
    >
      {children}
    </Text>
  );
};

const ButtonLink = ({ to, style, activeStyle, ...props }: ButtonLinkProps) => {
  const navigate = useNavigate();
  const path = to ?? '';
  const match = useMatch({ path, end: false });
  return (
    <Button
      className={() =>
        String(
          css({
            ...style,
            '&[data-pressed]': activeStyle,
            ...(match ? activeStyle : {}),
          }),
        )
      }
      {...props}
      variant={props.buttonVariant}
      onPress={e => {
        props.onPress?.(e);
        void navigate(path);
      }}
    />
  );
};

const InternalLink = ({
  to,
  style,
  activeStyle,
  children,
  isDisabled,
  isExactPathMatch = false,
}: InternalLinkProps) => {
  const path = to ?? '';
  const match = useMatch({ path, end: isExactPathMatch });

  return (
    <NavLink
      to={path}
      className={css([styles.smallText, style, match ? activeStyle : null])}
      onClick={e => {
        if (isDisabled) {
          e.preventDefault();
        }
      }}
    >
      {children}
    </NavLink>
  );
};

type LinkProps =
  | ({ variant: 'button' } & ButtonLinkProps)
  | ({ variant: 'internal' } & InternalLinkProps)
  | ({ variant: 'external' } & ExternalLinkProps)
  | ({ variant: 'text' } & TextLinkProps);

export function Link(props: LinkProps) {
  switch (props.variant) {
    case 'internal': {
      const { variant: _, ...internalProps } = props;
      return <InternalLink {...internalProps} />;
    }

    case 'external': {
      const { variant: _, ...externalProps } = props;
      return <ExternalLink {...externalProps} />;
    }

    case 'button': {
      const { variant: _, ...buttonProps } = props;
      return <ButtonLink {...buttonProps} />;
    }

    case 'text': {
      const { variant: _, ...textProps } = props;
      return <TextLink {...textProps} />;
    }

    default:
      throw new Error(`Unrecognised Link variant.`);
  }
}
