import React, { forwardRef } from "react";
import {
  Typography as MUITypography,
  type TypographyProps as MUITypographyProps,
} from "@mui/material";
import { twJoin } from "tailwind-merge";

import type { FontSize, FontWeight } from "../../theme/types";
import { twMerge } from "../../../tailwind.config";

export interface TypographyProps
  extends Omit<MUITypographyProps, "color" | "className"> {
  children?: React.ReactNode;
  color?: "main" | "secondary" | "action" | "success" | "danger" | "inherit";
  size?: FontSize;
  weight?: FontWeight;
  renderAs?: "span" | "p" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
  uppercase?: boolean;
  mode?: "light" | "dark";
  ESCAPE_className?: string;
}

const colors: Record<
  NonNullable<TypographyProps["color"]>,
  Record<NonNullable<TypographyProps["mode"]>, string>
> = {
  main: { light: "text-N700", dark: "text-N0" },
  secondary: { light: "text-N100", dark: "text-N60" },
  action: { light: "text-B400", dark: "text-B200" },
  success: { light: "text-G400", dark: "text-G200" },
  danger: { light: "text-R400", dark: "text-R200" },
  inherit: { light: "text-inherit", dark: "text-inherit" },
};

export const Typography = forwardRef<
  HTMLSpanElement | HTMLParagraphElement | HTMLHeadingElement,
  TypographyProps
>(
  (
    {
      children,
      color = "main",
      size = "200",
      weight = "regular",
      renderAs = "span",
      uppercase = false,
      mode = "light",
      noWrap = false,
      ESCAPE_className,
      ...restProps
    },
    ref
  ) => {
    const variant =
      renderAs === "span" ? "body2" : renderAs === "p" ? "body1" : renderAs;

    return (
      <MUITypography
        classes={{
          root: twJoin(
            `text-style-${weight.charAt(0).toUpperCase()}${size}`,
            twMerge(
              !noWrap && "whitespace-normal",
              colors[color][mode],
              uppercase && "uppercase",
              ESCAPE_className
            )
          ),
        }}
        variant={variant}
        noWrap={noWrap}
        {...restProps}
        ref={ref}
      >
        {children}
      </MUITypography>
    );
  }
);

Typography.displayName = "Typography";
