import Link, { LinkProps } from 'next/link';
import React, {
  forwardRef,
  ReactNode,
  useCallback,
  useMemo,
  useState,
} from 'react';
import {
  StyleSheet,
  Text as RNText,
  TextProps as RNTextProps,
  TouchableOpacity,
  TouchableOpacityProps,
} from 'react-native';
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,
  },
  button: {
    ...theme.fontSize.medium,
    backgroundColor: theme.color.green,
    borderRadius: theme.spacing.smaller,
    color: theme.color.foreground,
    fontWeight: theme.fontWeight.medium,
    marginVertical: theme.spacing.smaller,
    paddingHorizontal: theme.spacing.small,
    paddingVertical: theme.spacing.smaller,
    textAlign: 'center',
  },
  smallButton: {
    ...theme.fontSize.small,
    backgroundColor: theme.color.green,
    borderRadius: theme.spacing.smaller,
    color: theme.color.foreground,
    fontWeight: theme.fontWeight.medium,
    paddingHorizontal: theme.spacing.small,
    textAlign: 'center',
  },
});

/**
 * It's like Next LinkProps except href is optional and passHref with prefetch are
 * 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 spread.
 */
interface NextLinkSomeProps {
  href?: LinkProps['href'];
  as?: LinkProps['as'];
  replace?: LinkProps['replace'];
  scroll?: LinkProps['scroll'];
  shallow?: LinkProps['shallow'];
  // 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;
}

// Only props I use.
interface TouchableOpacitySomeProps {
  onPress?: TouchableOpacityProps['onPress'];
  onPressIn?: TouchableOpacityProps['onPressIn'];
  disabled?: TouchableOpacityProps['disabled'];
}

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

export type TextProps = RNTextProps &
  NextLinkSomeProps &
  TouchableOpacitySomeProps &
  TextOwnProps;

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

// Next.js needs ref for Intersection Observer based prefetching.
// That's how we delegate it to React Native for Web Text component.
const RNTextWithDOMRef = forwardRef(
  (props: RNTextProps & { children: ReactNode }, ref) => {
    return (
      <RNText
        {...props}
        // @ts-ignore RNfW prop.
        forwardedRef={ref}
      />
    );
  },
);

export const Text = ({
  // TextNextLinkProps
  href,
  as,
  replace,
  scroll,
  shallow,
  // TouchableOpacitySomeProps
  onPress,
  onPressIn,
  disabled,
  // 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',
        onMouseEnter,
        onMouseLeave,
      })}
      style={[
        variantStyle,
        colorStyle,
        props.style,
        hasHover && styles.linkHover,
      ]}
    >
      {children || '…'}
    </RNTextWithDOMRef>
  );

  // TODO: Handle external hrefs, use Platform.select({ web: { href, target: '_blank' } }).
  if (href)
    return (
      <Link {...{ href, as, replace, scroll, shallow, passHref: true }}>
        {text}
      </Link>
    );

  if (onPress || onPressIn || disabled) {
    return (
      <TouchableOpacity
        accessibilityRole="button"
        {...{ onPress, onPressIn, disabled }}
        {...(disabled && { style: { opacity: theme.opacity.disabled } })}
      >
        {text}
      </TouchableOpacity>
    );
  }

  return text;
};
