import Link from "next/link"

import Image from "@/components/Image"

import styles from "./jsontohtml.module.css"

import type { EmbedByUid } from "@/types/components/jsontohtml"
import { EmbedEnum } from "@/types/requests/utils/embeds"
import type { Attributes } from "@/types/rte/attrs"
import { RTEItemTypeEnum, RTETypeEnum } from "@/types/rte/enums"
import type {
  RTEDefaultNode,
  RTENext,
  RTENode,
  RTERegularNode,
} from "@/types/rte/node"
import { RTEMarkType } from "@/types/rte/node"
import type { RenderOptions } from "@/types/rte/option"

function extractPossibleAttributes(attrs: Attributes | undefined) {
  if (!attrs) return {}
  const props: Record<string, any> = {}
  if (attrs.id) {
    props.id = attrs.id
  }

  if (attrs.class) {
    props.className = attrs.class
  } else if (attrs["class-name"]) {
    props.className = attrs["class-name"]
  } else if (attrs.classname) {
    props.className = attrs.classname
  } else if (attrs?.style?.["text-align"]) {
    props.style = {
      textAlign: attrs?.style?.["text-align"],
    }
  }

  return props
}

export const renderOptions: RenderOptions = {
  [RTETypeEnum.a]: (
    node: RTERegularNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    if (node.attrs.url) {
      const props = extractPossibleAttributes(node.attrs)
      return (
        <a
          {...props}
          href={node.attrs.url}
          target={node.attrs.target ?? "_blank"}
          key={node.uid}
        >
          {next(node.children, embeds, fullRenderOptions)}
        </a>
      )
    }
    return null
  },

  [RTETypeEnum.blockquote]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <blockquote key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </blockquote>
    )
  },

  [RTETypeEnum.code]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <code key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </code>
    )
  },

  [RTETypeEnum.embed]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    if (node.attrs.src) {
      props.src = node.attrs.src
    }
    if (node.attrs.url) {
      props.src = node.attrs.url
    }
    if (!props.src) {
      return null
    }
    return (
      <iframe key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </iframe>
    )
  },

  [RTETypeEnum.h1]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <h1 key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </h1>
    )
  },

  [RTETypeEnum.h2]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <h2 key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </h2>
    )
  },

  [RTETypeEnum.h3]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <h3 key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </h3>
    )
  },

  [RTETypeEnum.h4]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <h4 key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </h4>
    )
  },

  [RTETypeEnum.h5]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <h5 key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </h5>
    )
  },

  [RTETypeEnum.h6]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <h6 key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </h6>
    )
  },

  [RTETypeEnum.hr]: () => {
    return <hr />
  },

  [RTETypeEnum.li]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <li key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </li>
    )
  },

  [RTETypeEnum.ol]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <ol key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </ol>
    )
  },

  [RTETypeEnum.p]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <p {...props} key={node.uid}>
        {next(node.children, embeds, fullRenderOptions)}
      </p>
    )
  },

  [RTETypeEnum.reference]: (
    node: RTENode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    if ("attrs" in node) {
      const type = node.attrs.type
      if (type === RTEItemTypeEnum.asset) {
        const image = embeds?.[node?.attrs?.["asset-uid"]]
        if (image?.node.__typename === EmbedEnum.SysAsset) {
          const alt = image?.node?.title ?? node.attrs.alt
          const alignment = node.attrs?.style?.["text-align"]
            ? {
                alignSelf: node.attrs?.style?.["text-align"],
              }
            : {}
          return (
            <Image
              key={node.uid}
              alt={alt}
              className={styles.image}
              height={image.node.dimension.height}
              src={image?.node?.url}
              width={image.node.dimension.width}
              style={alignment}
            />
          )
        }
      } else {
        const props = extractPossibleAttributes(node.attrs)
        const href = node.attrs?.locale
          ? `/${node.attrs.locale}${node.attrs.href}`
          : node.attrs.href
        return (
          <Link {...props} href={href} key={node.uid}>
            {next(node.children, embeds, fullRenderOptions)}
          </Link>
        )
      }
    }

    return null
  },

  [RTETypeEnum.table]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <table key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </table>
    )
  },

  [RTETypeEnum.thead]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <thead key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </thead>
    )
  },

  [RTETypeEnum.tbody]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <tbody key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </tbody>
    )
  },

  [RTETypeEnum.tfoot]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <tfoot key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </tfoot>
    )
  },

  [RTETypeEnum.tr]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <tr key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </tr>
    )
  },

  [RTETypeEnum.th]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <th key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </th>
    )
  },

  [RTETypeEnum.td]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <td key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </td>
    )
  },

  [RTETypeEnum.ul]: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    const props = extractPossibleAttributes(node.attrs)
    return (
      <ul key={node.uid} {...props}>
        {next(node.children, embeds, fullRenderOptions)}
      </ul>
    )
  },

  /** TextNode wrappers */
  [RTEMarkType.bold]: (children: React.ReactNode) => {
    return <strong>{children}</strong>
  },

  [RTEMarkType.italic]: (children: React.ReactNode) => {
    return <em>{children}</em>
  },

  [RTEMarkType.underline]: (children: React.ReactNode) => {
    return <u>{children}</u>
  },

  [RTEMarkType.strikethrough]: (children: React.ReactNode) => {
    return <s>{children}</s>
  },

  [RTEMarkType.inlineCode]: (children: React.ReactNode) => {
    return <span>{children}</span>
  },

  [RTEMarkType.subscript]: (children: React.ReactNode) => {
    return <sub>{children}</sub>
  },

  [RTEMarkType.superscript]: (children: React.ReactNode) => {
    return <sup>{children}</sup>
  },

  [RTEMarkType.break]: (children: React.ReactNode) => {
    return (
      <>
        <br />
        {children}
      </>
    )
  },

  [RTEMarkType.classnameOrId]: (
    children: React.ReactNode,
    className?: string,
    id?: string
  ) => {
    let props = {
      className,
      id,
    }
    if (!className) {
      delete props.className
    }
    if (!id) {
      delete props.id
    }
    return (
      <span key={id} {...props}>
        {children}
      </span>
    )
  },

  /**
   * Contentstack can return something called `default` as seen here in their
   * own SDK (https://github.com/contentstack/contentstack-utils-javascript/blob/master/src/options/default-node-options.ts#L89)
   */
  default: (
    node: RTEDefaultNode,
    embeds: EmbedByUid,
    next: RTENext,
    fullRenderOptions: RenderOptions
  ) => {
    return next(node.children, embeds, fullRenderOptions)
  },
}
