import { type BlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'
import clsx from 'clsx'
import Image from 'next/image'
import { type FC } from 'react'

import Callout, { CalloutIcon } from '@/components/server/Callout'
import CodeSnippet, { SyncCodeSnippet } from '@/components/server/CodeSnippet'
import List from '@/components/server/List'
import RichText from '@/components/server/RichText'
import TeX, { SyncTeX } from '@/components/server/TeX'
import { type Block, type BlockType } from '@/lib/notion/types'
import extractCurrentListItems from '@/lib/notion/utils/extractCurrentListItems'
import getProxiedAssetUrl from '@/lib/notion/utils/getProxiedAssetUrl'
import richTextAsPlainText from '@/lib/notion/utils/richTextToPlainText'

export const blockRenderMap: {
  [T in BlockType]: FC<{ block: Block<T> }>
} = {
  paragraph: ({ block }) => (
    <p className="mb-6 text-justify">
      <RichText richText={block.paragraph.rich_text} />
    </p>
  ),
  heading_1: ({ block }) => (
    <>
      <h2
        id={block.id}
        className="font-display mb-6 text-4xl before:-mt-40 before:block before:h-40"
      >
        <RichText richText={block.heading_1.rich_text} />
      </h2>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.has_children && <RenderBlockList blockList={block.children} />}
    </>
  ),
  heading_2: ({ block }) => (
    <>
      <h3
        id={block.id}
        className="font-display mb-5 text-2xl before:-mt-40 before:block before:h-40"
      >
        <RichText richText={block.heading_2.rich_text} />
      </h3>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.has_children && <RenderBlockList blockList={block.children} />}
    </>
  ),
  heading_3: ({ block }) => (
    <>
      <h4
        id={block.id}
        className="font-display mb-5 text-xl before:-mt-40 before:block before:h-40"
      >
        <RichText richText={block.heading_3.rich_text} />
      </h4>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.has_children && <RenderBlockList blockList={block.children} />}
    </>
  ),
  image: ({ block }) => {
    const caption = block.image.caption
    const altText = caption[0]?.plain_text.startsWith('alt: ')
      ? richTextAsPlainText(caption).substring(5)
      : richTextAsPlainText(caption)
    return (
      <figure className="mb-6">
        <Image
          alt={altText}
          className={clsx('h-auto w-full rounded object-cover', {
            'mb-2': block.image.caption.length > 0,
          })}
          src={getProxiedAssetUrl({
            blockId: block.id,
            lastEditedTime: block.last_edited_time,
          })}
          width={9999} // TODO
          height={9999} // TODO
        />
        {block.image.caption.length > 0 && (
          <figcaption className="text-sm font-light">
            <RichText richText={block.image.caption} />
          </figcaption>
        )}
      </figure>
    )
  },
  bulleted_list_item: ({ block }) => (
    <li>
      <RichText richText={block.bulleted_list_item.rich_text} />
    </li>
  ),
  numbered_list_item: ({ block }) => (
    <li>
      <RichText richText={block.numbered_list_item.rich_text} />
    </li>
  ),
  quote: ({ block }) => (
    <blockquote className="border-l-1 mb-6 bg-gradient-to-r from-[#171717] px-10 py-7 text-justify dark:[border-image:linear-gradient(to_bottom,rgba(255,255,255,1),rgba(255,255,255,0))_1]">
      <RichText richText={block.quote.rich_text} />
    </blockquote>
  ),
  toggle: ({ block }) => (
    <details>
      <summary>
        <RichText richText={block.toggle.rich_text} />
      </summary>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.children && <RenderBlockList blockList={block.children} />}
    </details>
  ),
  callout: ({ block }) => (
    <Callout>
      <CalloutIcon icon={block.callout.icon} />
      <RichText richText={block.callout.rich_text} />
    </Callout>
  ),
  // @ts-expect-error Server Component
  equation: ({ block }) => <TeX block math={block.equation.expression} />,
  divider: () => (
    <hr className="my-9 [border-image:linear-gradient(to_right,#424242,#FFFFFF00)_1]" />
  ),
  code: ({ block }) => (
    // @ts-expect-error Server Component
    <CodeSnippet
      language={block.code.language}
      codeRichText={block.code.rich_text}
    />
  ),

  // TODO?
  table_of_contents: ({ block }) => <></>,
  column_list: ({ block }) => <></>,
  column: ({ block }) => <></>,
  link_to_page: ({ block }) => <></>,
  table: ({ block }) => <></>,
  table_row: ({ block }) => <></>,
  embed: ({ block }) => <></>,
  bookmark: ({ block }) => <></>,
  video: ({ block }) => <></>,
  pdf: ({ block }) => <></>,
  file: ({ block }) => <></>,
  audio: ({ block }) => <></>,
  link_preview: ({ block }) => <></>,
  breadcrumb: ({ block }) => <></>,
  synced_block: ({ block }) => <></>,
  template: ({ block }) => <></>,
  to_do: ({ block }) => <></>,
  child_page: ({ block }) => <></>,
  child_database: ({ block }) => <></>,
  unsupported: ({ block }) => <></>,
}

export const rssBlockRenderMap: typeof blockRenderMap = {
  ...blockRenderMap,
  paragraph: ({ block }) => (
    <p className="mb-6 text-justify">
      <RichText rss richText={block.paragraph.rich_text} />
    </p>
  ),
  heading_1: ({ block }) => (
    <>
      <h2
        id={block.id}
        className="font-display mb-6 text-4xl before:-mt-40 before:block before:h-40"
      >
        <RichText rss richText={block.heading_1.rich_text} />
      </h2>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.has_children && <RenderBlockList blockList={block.children} />}
    </>
  ),
  heading_2: ({ block }) => (
    <>
      <h3
        id={block.id}
        className="font-display mb-5 text-2xl before:-mt-40 before:block before:h-40"
      >
        <RichText rss richText={block.heading_2.rich_text} />
      </h3>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.has_children && <RenderBlockList blockList={block.children} />}
    </>
  ),
  heading_3: ({ block }) => (
    <>
      <h4
        id={block.id}
        className="font-display mb-5 text-xl before:-mt-40 before:block before:h-40"
      >
        <RichText rss richText={block.heading_3.rich_text} />
      </h4>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.has_children && <RenderBlockList blockList={block.children} />}
    </>
  ),
  image: ({ block }) => {
    const caption = block.image.caption
    const altText = caption[0]?.plain_text.startsWith('alt: ')
      ? richTextAsPlainText(caption).substring(5)
      : richTextAsPlainText(caption)
    return (
      <figure className="mb-6">
        {/* eslint-disable-next-line @next/next/no-img-element */}
        <img
          alt={altText}
          className={clsx('h-auto w-full rounded object-cover', {
            'mb-2': block.image.caption.length > 0,
          })}
          src={getProxiedAssetUrl({
            blockId: block.id,
            lastEditedTime: block.last_edited_time,
          })}
        />
        {block.image.caption.length > 0 && (
          <figcaption className="text-sm font-light">
            <RichText rss richText={block.image.caption} />
          </figcaption>
        )}
      </figure>
    )
  },
  bulleted_list_item: ({ block }) => (
    <li>
      <RichText rss richText={block.bulleted_list_item.rich_text} />
    </li>
  ),
  numbered_list_item: ({ block }) => (
    <li>
      <RichText rss richText={block.numbered_list_item.rich_text} />
    </li>
  ),
  quote: ({ block }) => (
    <blockquote className="border-l-1 mb-6 bg-gradient-to-r from-[#171717] px-10 py-7 text-justify dark:[border-image:linear-gradient(to_bottom,rgba(255,255,255,1),rgba(255,255,255,0))_1]">
      <RichText rss richText={block.quote.rich_text} />
    </blockquote>
  ),
  toggle: ({ block }) => (
    <details>
      <summary>
        <RichText rss richText={block.toggle.rich_text} />
      </summary>
      {/* @ts-expect-error TODO find a way to fix this type */}
      {block.children && <RenderBlockList blockList={block.children} />}
    </details>
  ),
  callout: ({ block }) => (
    <Callout>
      <CalloutIcon icon={block.callout.icon} />
      <RichText rss richText={block.callout.rich_text} />
    </Callout>
  ),
  equation: ({ block }) => <SyncTeX block math={block.equation.expression} />,
  code: ({ block }) => (
    <SyncCodeSnippet
      language={block.code.language}
      codeRichText={block.code.rich_text}
    />
  ),
}

export const RenderBlockList = ({
  blockList,
  rss = false,
}: {
  blockList: BlockObjectResponse[]
  rss?: boolean
}) => {
  return (
    <>
      {blockList.map((block, index, list) => {
        const isListItem =
          block.type === 'bulleted_list_item' ||
          block.type === 'numbered_list_item'

        if (!isListItem)
          return <RenderBlock rss={rss} key={block.id} block={block} />
        else if (list[index - 1].type !== block.type)
          return (
            <List
              key={block.id}
              as={block.type === 'bulleted_list_item' ? 'ul' : 'ol'}
            >
              {extractCurrentListItems({ list, currentIndex: index }).map(
                (listItem) => (
                  <RenderBlock rss={rss} key={listItem.id} block={listItem} />
                ),
              )}
            </List>
          )
      })}
    </>
  )
}

export function RenderBlock({
  block,
  rss = false,
}: {
  block: BlockObjectResponse
  rss?: boolean
}) {
  const Component = (rss ? rssBlockRenderMap : blockRenderMap)[block.type]
  // @ts-expect-error we know this is correct because `block.type` is the name we want to use as a key for `blockRenderMap`
  return <Component block={block} />
}
