import Link from 'next/link';
import React, {
  forwardRef,
  ReactNode,
  useCallback,
  useMemo,
  useState,
} from 'react';
import {
  StyleSheet,
  Text as RNText,
  TextProps as RNTextProps,
} from 'react-native';
import { UrlObject } from 'url';
import { useStyles } from '../hooks/useStyles';
import { useTheme } from '../hooks/useTheme';
import { Theme } from '../themes/theme';

const createStyles = (theme: Theme) => ({
  h1: {
    ...theme.fontSize.big,
    color: theme.color.background,
    marginBottom: theme.spacing.medium,
  },
  h2: {
    ...theme.fontSize.medium,
    color: theme.color.background,
    fontWeight: theme.fontWeight.medium,
    marginBottom: theme.spacing.medium,
  },
  text: {
    ...theme.fontSize.medium,
    color: theme.color.background,
  },
  p: {
    ...theme.fontSize.medium,
    color: theme.color.background,
    marginBottom: theme.spacing.medium,
  },
  small: {
    ...theme.fontSize.small,
    color: theme.color.background,
  },
  smallRed: {
    ...theme.fontSize.small,
    color: theme.color.red,
  },
  smallBold: {
    ...theme.fontSize.small,
    color: theme.color.background,
    fontWeight: theme.fontWeight.medium,
  },
  smallBoldRed: {
    ...theme.fontSize.small,
    color: theme.color.red,
    fontWeight: theme.fontWeight.medium,
  },
  li: {
    ...theme.fontSize.medium,
    color: theme.color.background,
    marginBottom: theme.spacing.small,
  },
  link: {
    ...theme.fontSize.medium,
    color: theme.color.blue,
  },
  code: {
    ...theme.fontSize.small,
    fontFamily: theme.fontFamily.monospace,
  },
  // Consolas, ‘Andale Mono WT’, ‘Andale Mono’, ‘Lucida Console’, ‘Lucida Sans Typewriter’, ‘DejaVu Sans Mono’, ‘Bitstream Vera Sans Mono’, ‘Liberation Mono’, ‘Nimbus Mono L’, Monaco, ‘Courier New’, Courier, monospace
});

/**
 * It's like Next LinkProps except href is optional and passHref with prefetch not allowed.
 * The idea is simple. Because anchor is rendered via Text in RNfW,
 * we just added Next Link functionality into Text.
 * We had to copy-paste LinkProps, because props must be handled explicitly.
 */
interface TextNextLinkProps {
  href?: string | UrlObject;
  as?: string | UrlObject;
  replace?: boolean;
  scroll?: boolean;
  shallow?: boolean;
  // Next.js auto-prefetches automatically based on viewport. The prefetch attribute is
  // no longer needed. More: https://err.sh/zeit/next.js/prefetch-true-deprecated
  // prefetch?: boolean;
}

interface TextOwnProps {
  variant: keyof ReturnType<typeof createStyles>;
  color?: keyof Theme['color'];
  children: ReactNode;
}

export type TextProps = RNTextProps & TextNextLinkProps & TextOwnProps;

const styles = StyleSheet.create({
  linkHover: {
    textDecorationLine: 'underline',
  },
});

const RNTextWithDOMRef = forwardRef(
  (props: RNTextProps & { children: ReactNode }, ref) => {
    return (
      <RNText
        {...props}
        // @ts-ignore RNfW props.
        forwardedRef={ref}
      />
    );
  },
);

// TODO: isExternal auto-detection then Platform.select({ web: { href, target: '_blank' } }).

export const Text = ({
  // TextNextLinkProps
  href,
  as,
  replace,
  scroll,
  shallow,
  // TextOwnProps
  variant,
  color,
  children,
  ...props
}: TextProps) => {
  const variantStyle = useStyles(createStyles)[variant];
  const theme = useTheme();
  const colorStyle = useMemo(
    () =>
      color &&
      StyleSheet.create({ color: { color: theme.color[color] } }).color,
    [color, theme.color],
  );
  const [hasHover, setHasHover] = useState(false);

  const onMouseEnter = useCallback(() => {
    setHasHover(true);
  }, []);

  const onMouseLeave = useCallback(() => {
    setHasHover(false);
  }, []);

  const text = (
    <RNTextWithDOMRef
      {...props}
      {...(href && {
        accessibilityRole: 'link',
        href,
        onMouseEnter,
        onMouseLeave,
      })}
      style={[
        variantStyle,
        colorStyle,
        props.style,
        hasHover && styles.linkHover,
      ]}
    >
      {children || '…'}
    </RNTextWithDOMRef>
  );

  if (!href) return text;

  return <Link {...{ href, as, replace, scroll, shallow }}>{text}</Link>;
};
