import React, { forwardRef } from 'react';
import styled, { css, ThemeProvider } from 'styled-components';

import { outputFunctions, isString, isNumber, assertUnit, parseValue } from './system';

/* from REACT CN: https://react-cn.github.io/react/docs/tags-and-attributes.html */

const svgAttrs = 'clipPath cx cy d dx dy fill fillOpacity fontFamily fontSize fx fy gradientTransform gradientUnits markerEnd markerMid markerStart offset opacity patternContentUnits patternUnits points preserveAspectRatio r rx ry spreadMethod stopColor stopOpacity stroke  strokeDasharray strokeLinecap strokeOpacity strokeWidth textAnchor transform version viewBox x1 x2 x xlinkActuate xlinkArcrole xlinkHref xlinkRole xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace y1 y2 y'
  .split(/\s/)
  .filter((s) => s);

const htmlAttrs = 'accept acceptCharset accessKey action allowFullScreen allowTransparency alt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing charSet challenge checked classID className cols colSpan content contentEditable contextMenu controls coords crossOrigin data dateTime defer dir disabled download draggable encType form formAction formEncType formMethod formNoValidate formTarget frameBorder headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode keyParams keyType label lang list loop low manifest marginHeight marginWidth max maxLength media mediaGroup method min minLength multiple muted name noValidate open optimum pattern placeholder poster preload radioGroup readOnly rel required role rows rowSpan sandbox scope scoped scrolling seamless selected shape size sizes span spellCheck src srcDoc srcSet start step style summary tabIndex target title type useMap value width wmode wrap'
  .concat(
    ' ',
    [
      'autoCapitalize autoCorrect', // for Mobile Safari.
      'property', // for Open Graph meta tags.
      'itemProp itemScope itemType itemRef itemID', // for HTML5 microdata.
      'unselectable', // for Internet Explorer.
      'results autoSave', // for WebKit/Blink input fields of type search.
    ].join(' '),
  )
  .split(/\s/);

const dimensionPropKeys = [
  // primary
  'd',
  'pos',
  'w',
  'minw',
  'maxw',
  'h',
  'minh',
  'maxh',
  'my',
  'mx',
  'mt',
  'mr',
  'mb',
  'ml',
  'py',
  'px',
  'pt',
  'pr',
  'pb',
  'pl',
  // special
  'span',
  // secondary
  'fx',
  'fd',
  'ta',
  'ai',
  'ac',
  'jc',
  'order',
  'fs',
  'fw',
  'ws',
  'pe',
  'us',
  'cur',
  'fg',
  'bd',
  'ol',
  'bg',
];

export const handleDimensions = (props) =>
  dimensionPropKeys.map((key) => props[key] && outputFunctions[key](props[key]));

const typoPropKeys = ['f', 'fa', 'fz', 'lh'];

export const handleTypography = ({ theme: { fontSpecs, current }, f, fa, fz, lh }) => {
  // console.log(`current fontAlias is ${current.fontAlias}`);

  const fontAlias = f || fa ? fa || f?.split('-')[0] : undefined;
  const fontSize = f || fz ? fz || f?.split('-')[1] : undefined;
  const lineHeight =
    fontAlias || fontSize || lh
      ? lh && isNumber(lh)
        ? assertUnit(lh, 'em')
        : fontSize && isString(fontSize)
        ? `var(--line-height-${fontAlias || current.fontAlias}-${fontSize}, ${assertUnit(
            fontSpecs[fontAlias || current.fontAlias].lineHeight || 1.15,
            'em',
          )})`
        : fontAlias
        ? assertUnit(fontSpecs[fontAlias || current.fontAlias].lineHeight || 1.15, 'em')
        : undefined
      : undefined;

  const rules = [
    fontSize &&
      outputFunctions.fz(
        isString(fontSize) ? `var(--font-size-${fontAlias || current.fontAlias}-${fontSize})` : fontSize,
        (n) => assertUnit(n, 'px'),
      ),
    lineHeight && outputFunctions.lh(lineHeight),
  ];

  if (fontAlias && fontAlias !== current.fontAlias) {
    const font = fontSpecs[fontAlias];
    rules.push(
      ...[
        `--font-family: ${font.fontFamily};`,
        font.letterSpacing && `--letter-spacing: ${assertUnit(font.letterSpacing, 'em')};`,
        font.trimCapline && `--trim-capline: ${assertUnit(font.trimCapline, 'em')};`,
        font.trimBaseline && `--trim-baseline: ${assertUnit(font.trimBaseline, 'em')};`,
        font.trimSides && `--trim-sides: ${assertUnit(font.trimSides, 'em')};`,
        font.trimAdjacent && `--trim-adjacent: ${assertUnit(font.trimAdjacent, 'em')};`,
      ],
    );
  }

  // console.log(`current fontAlias is ${current.fontAlias}`);
  return rules.filter((n) => n);
};

export const BoxEl = styled.div
  .attrs(({ f, fa, fz, lh }) => ({
    className: f || fa || fz || lh ? 'font-node' : '',
  }))
  .withConfig({
    shouldForwardProp: (key) =>
      key === 'children' || htmlAttrs.includes(key) || key.match(/^(aria|data)-/)?.length > 0,
  })`
    ${handleDimensions}
    ${handleTypography}
`;

export const Box = forwardRef((props, ref) => {
  const { f, fa: fontAlias = f?.split('-')[0] } = props;
  if (!fontAlias) return <BoxEl ref={ref} {...props} />;
  const { children, ...restProps } = props;
  return (
    <BoxEl ref={ref} {...restProps}>
      <ThemeProvider
        theme={({ current: { ...currentRest }, ...themeRest }) => ({
          ...themeRest,
          current: { ...currentRest, fontAlias },
        })}
      >
        {children}
      </ThemeProvider>
    </BoxEl>
  );
});
Box.displayName = 'Box';

// export const Stop = forwardRef((props, ref) => <Box ref={ref} {...props} py={0.1} />);
// Stop.displayName = 'Stop';

export const Header = styled(Box).attrs({ forwardedAs: 'header', role: 'banner' })``;
export const Footer = styled(Box).attrs({ forwardedAs: 'footer', role: 'contentinfo' })``;
export const Main = styled(Box).attrs({ forwardedAs: 'main' })``;

export const Span = styled(Box).attrs({ forwardedAs: 'span' })``;
export const Link = styled(Box).attrs({ forwardedAs: 'a' })``;
export const Button = styled(Box).attrs({ forwardedAs: 'button' })``;

export const Stop = styled(Box)`
  padding: 0.1px;
`;
export const Hold = styled(Box)`
  position: relative;
`;

// https://css-tricks.com/a-first-look-at-aspect-ratio/
export const Aspect = styled(Box)`
  position: relative;
  & > :first-child {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
  ${({ w, h, ratio = 1 }) => {
    if (!w && !h) {
      return css`
        &::after {
          content: '';
          display: block;
          width: 100%;
          padding-bottom: ${100 / ratio}%;
        }
      `;
    } else if (!h) {
      const { value: width, unit } = parseValue(w);
      return css`
        height: ${`${width / ratio}${unit || 'px'}`};
      `;
    } else if (!w) {
      const { value: height, unit } = parseValue(h);
      return css`
        width: ${`${height * ratio}${unit || 'px'}`};
      `;
    }
  }}
`;

/*
  ICON
  span, inline-block
 */

export const Section = forwardRef((props, ref) => {
  const { f, fa: newFontAlias = f?.split('-')[0] } = props;
  const { as = 'section', children, ...restProps } = props;
  return (
    <BoxEl as={as} ref={ref} {...restProps}>
      <ThemeProvider
        theme={({ current: { hLevel, fontAlias, ...currentRest }, ...themeRest }) => ({
          ...themeRest,
          current: {
            ...currentRest,
            hLevel: Math.min(6, hLevel + 1),
            fontAlias: newFontAlias || fontAlias,
          },
        })}
      >
        {children}
      </ThemeProvider>
    </BoxEl>
  );
});
Section.displayName = 'Section';

export const Nav = styled(Section).attrs({ forwardedAs: 'nav' })``;
export const Aside = styled(Section).attrs({ forwardedAs: 'aside' })``;
export const Article = styled(Section).attrs({ forwardedAs: 'article' })``;

// export const Nav = forwardRef((props, ref) => <Section as="nav" ref={ref} {...props} />);
// Nav.displayName = 'Nav';

// export const Aside = forwardRef((props, ref) => <Section as="aside" ref={ref} {...props} />);
// Aside.displayName = 'Aside';

// export const Article = forwardRef((props, ref) => <Section as="article" ref={ref} {...props} />);
// Article.displayName = 'Article';
