import React from 'react';
import { H1, H2, H3, H4, H5 } from '@cloudflare/elements';
import { createStyledComponent, ThemeProp } from '@cloudflare/style-container';

type SectionProps = {
  children: React.ReactNode;
  level?: number;
  offset?: number;
};

type HeadingProps = React.ComponentProps<typeof H1> & {
  level?: number;
  offset?: number;
  role?: string;
};

const HeadingInfo = React.createContext({
  level: 0,
  offset: 0
});

const headerThemes = [
  ({ theme }: { theme: ThemeProp['theme'] }) => ({
    fontSize: theme.fontSizes[6],
    lineHeight: 1.25,
    color: theme.colors.gray[1],
    fontWeight: 600
  }),
  ({ theme }: { theme: ThemeProp['theme'] }) => ({
    fontSize: theme.fontSizes[5],
    lineHeight: 1.25,
    color: theme.colors.gray[3],
    fontWeight: 400
  }),
  ({ theme }: { theme: ThemeProp['theme'] }) => ({
    fontSize: theme.fontSizes[4],
    fontWeight: 600
  }),
  ({ theme }: { theme: ThemeProp['theme'] }) => ({
    fontSize: theme.fontSizes[3],
    lineHeight: 1.25,
    fontWeight: 600
  })
];

const headings = [H1, H2, H3, H4, H5];

const maxHeadingLevel = headings.length - 1;

const maxOffset = 2;

const standardHeadings = headings.map((h, index) => {
  const themeIndex = Math.min(index, headerThemes.length);
  return createStyledComponent(headerThemes[themeIndex], h);
});

// Renders H1 with H2 style, H2 with H3 style etc.
const offsetOneHeadings = headings.map((h, index) => {
  const themeIndex = Math.min(index + 1, headerThemes.length);
  return createStyledComponent(headerThemes[themeIndex], h);
});

// Renders H1 with H3 style, H2 with H4 style etc.
const offsetTwoHeadings = headings.map((h, index) => {
  const themeIndex = Math.min(index + 2, headerThemes.length);
  return createStyledComponent(headerThemes[themeIndex], h);
});

export function Section(props: SectionProps) {
  return (
    <HeadingInfo.Consumer>
      {info => (
        <HeadingInfo.Provider
          value={{
            level: props.level ?? info.level + 1,
            offset: info.offset + (props.offset ?? 0)
          }}
        >
          {props.children}
        </HeadingInfo.Provider>
      )}
    </HeadingInfo.Consumer>
  );
}

export function Heading({ level, offset = 0, role, ...props }: HeadingProps) {
  return (
    <HeadingInfo.Consumer>
      {info => {
        const headingOffset = Math.min(maxOffset, info.offset + offset);
        const headings =
          headingOffset === 1
            ? offsetOneHeadings
            : headingOffset === 2
            ? offsetTwoHeadings
            : standardHeadings;

        const headingLevel = Math.min(
          level !== undefined ? level - 1 : info.level,
          maxHeadingLevel
        );
        const Heading = headings[Math.max(0, headingLevel)];
        // @ts-ignore
        return <Heading role={role} {...props} />;
      }}
    </HeadingInfo.Consumer>
  );
}
