import { Root } from "hast"
import "highlight.js/styles/base16/green-screen.css"
import mermaid from "mermaid"
import React, {
  Children,
  Fragment,
  createElement,
  isValidElement,
  useEffect,
  useMemo,
  useState,
  useRef,
} from "react"
import flattenChildren from "react-keyed-flatten-children"
import rehypeHighlight from "rehype-highlight"
import rehypeReact from "rehype-react"
import remarkGfm from "remark-gfm"
import remarkParse from "remark-parse"
import remarkRehype from "remark-rehype"
import { Plugin, unified } from "unified"
import { visit } from "unist-util-visit"
import styled, { css } from "styled-components"
import {
  ActionIconWrapper,
  dispatchToast,
  TooltipBox,
  Typography,
} from "@/components/shared/components"
import { copyToClipboard, minifyCurrencyValue } from "@/utils"
import { CopyIcon, TickIcon } from "@/icons"
import { useThemeContext } from "@/providers/themeProvider"
import {
  CodeBlockPre,
  UlDot,
  UlList,
  UlListItem,
} from "@/components/morphoStrategy/ai-components/ChatLayout/styles"
import { defaultTheme } from "@/lib"
export const ANCHOR_CLASS_NAME =
  "font-semibold underline text-emerald-700 underline-offset-[2px] decoration-1 hover:text-emerald-800 transition-colors"

// Mixing arbitrary Markdown + Capsize leads to lots of challenges
// with paragraphs and list items. This replaces paragraphs inside
// list items into divs to avoid nesting Capsize.
const rehypeListItemParagraphToDiv: Plugin<[], Root> = () => {
  return (tree) => {
    visit(tree, "element", (element) => {
      if (element.tagName === "li") {
        element.children = element.children.map((child) => {
          if (child.type === "element" && child.tagName === "p") {
            child.tagName = "div"
          }
          return child
        })
      }
    })
    return tree
  }
}

export const useMarkdownProcessor = (content: string) => {
  const { theme } = useThemeContext()

  useEffect(() => {
    mermaid.initialize({ startOnLoad: false, theme: "forest" })
  }, [])

  return useMemo(() => {
    return unified()
      .use(remarkParse)
      .use(remarkGfm)
      .use(remarkRehype)
      .use(rehypeHighlight, { ignoreMissing: true })
      .use(rehypeListItemParagraphToDiv)
      .use(rehypeReact, {
        createElement,
        Fragment,
        components: {
          a: ({ href, children }: JSX.IntrinsicElements["a"]) => (
            <a
              href={href}
              target="_blank"
              rel="noreferrer"
              className={ANCHOR_CLASS_NAME}
            >
              {children}
            </a>
          ),
          h1: ({ children, id }: JSX.IntrinsicElements["h1"]) => (
            <Typography type="TITLE_XXL" id={id}>
              {children}
            </Typography>
          ),
          h2: ({ children, id }: JSX.IntrinsicElements["h2"]) => (
            <Typography type="TITLE_XL" id={id}>
              {children}
            </Typography>
          ),
          h3: ({ children, id }: JSX.IntrinsicElements["h3"]) => (
            <Typography type="TITLE_L" id={id}>
              {children}
            </Typography>
          ),
          h4: ({ children, id }: JSX.IntrinsicElements["h4"]) => (
            <Typography type="TITLE_M" id={id}>
              {children}
            </Typography>
          ),
          h5: ({ children, id }: JSX.IntrinsicElements["h5"]) => (
            <Typography type="BODY_M" id={id}>
              {children}
            </Typography>
          ),
          h6: ({ children, id }: JSX.IntrinsicElements["h6"]) => (
            <Typography type="BODY_S" id={id}>
              {children}
            </Typography>
          ),
          p: (props: JSX.IntrinsicElements["p"]) => {
            return (
              <Typography color={theme.colors.gray400} type="BODY_MEDIUM_S">
                {props.children}
              </Typography>
            )
          },
          strong: ({ children }: JSX.IntrinsicElements["strong"]) => (
            <strong className="text-emerald-950 font-semibold">
              {children}
            </strong>
          ),
          em: ({ children }: JSX.IntrinsicElements["em"]) => (
            <em>{children}</em>
          ),
          // code: CodeBlock,
          // pre: ({ children }: JSX.IntrinsicElements["pre"]) => {
          //   return (
          //     <CodeBlock>
          //       <CodeBlockPre>{children}</CodeBlockPre>
          //     </CodeBlock>
          //   )
          // },
          ul: ({ children }: JSX.IntrinsicElements["ul"]) => (
            <UlList>
              {Children.map(
                flattenChildren(children).filter(isValidElement),
                (child, index) => (
                  <Typography>
                    <UlListItem key={index}>
                      <UlDot />
                      {child}
                    </UlListItem>
                  </Typography>
                ),
              )}
            </UlList>
          ),
          ol: ({ children }: JSX.IntrinsicElements["ol"]) => (
            <ol className="flex flex-col gap-3 text-emerald-900 my-6 pl-3 [&_ol]:my-3 [&_ul]:my-3">
              {Children.map(
                flattenChildren(children).filter(isValidElement),
                (child, index) => (
                  <Typography>
                    <li key={index} className="flex gap-2 items-start">
                      <div
                        className="font-sans text-sm text-emerald-900 font-semibold shrink-0 min-w-[1.4ch]"
                        aria-hidden
                      >
                        {index + 1}.
                      </div>
                      {child}
                    </li>
                  </Typography>
                ),
              )}
            </ol>
          ),
          li: ({ children }: JSX.IntrinsicElements["li"]) => (
            <Typography type="BODY_S">{children}</Typography>
          ),

          table: ({ children }: JSX.IntrinsicElements["table"]) => {
            const convertToTableData = (
              children: React.ReactNode,
            ): string[][] => {
              const rows: string[][] = []
              React.Children.forEach(children, (child) => {
                if (React.isValidElement(child)) {
                  React.Children.forEach(child.props.children, (row) => {
                    if (React.isValidElement(row) && row.type === "tr") {
                      const cells: string[] = []
                      React.Children.forEach(
                        (row.props as { children: React.ReactNode }).children,
                        (cell) => {
                          if (React.isValidElement(cell)) {
                            const cellContent = (
                              cell.props as { children: string[] }
                            ).children
                            cells.push(cellContent[0])
                          }
                        },
                      )
                      rows.push(cells)
                    }
                  })
                }
              })
              return rows
            }

            const tableData = convertToTableData(children)

            const vaultsTableHeaders = [
              "Vault Name",
              "Current APY ↓",
              "7-Day APY",
              "Supply",
              "Exit Liquidity",
              "Markets",
              "Curator",
            ]

            const isRenderingVaultsTable =
              Array.isArray(tableData[0]) &&
              tableData[0].length === vaultsTableHeaders.length &&
              tableData[0].every(
                (value, index) => value === vaultsTableHeaders[index],
              )

            const TRUNCATE_CELL_LENGTH = 24

            return (
              <StyledTable>
                {isRenderingVaultsTable ? (
                  <>
                    <thead>
                      <tr>
                        {vaultsTableHeaders.map((header, index) => (
                          <th key={index}>{header}</th>
                        ))}
                      </tr>
                    </thead>
                    <tbody>
                      {tableData.slice(1).map((row, rowIndex) => (
                        <tr key={rowIndex}>
                          {row.map((cell, cellIndex) => (
                            <td
                              style={{
                                ...(cell.length > TRUNCATE_CELL_LENGTH && {
                                  display: "flex",
                                  alignItems: "center",
                                }),
                              }}
                              key={cellIndex}
                            >
                              {cell.length > TRUNCATE_CELL_LENGTH ? (
                                <>
                                  {cell.substring(0, TRUNCATE_CELL_LENGTH)}...
                                  <TooltipBox
                                    content={
                                      cell.includes("USD")
                                        ? formatMarketStringToTable(cell)
                                        : cell
                                    }
                                  />
                                </>
                              ) : (
                                cell
                              )}
                            </td>
                          ))}
                        </tr>
                      ))}
                    </tbody>
                  </>
                ) : (
                  children
                )}
              </StyledTable>
            )
          },
          thead: ({ children }: JSX.IntrinsicElements["thead"]) => (
            <thead className="bg-emerald-100">{children}</thead>
          ),
          th: ({ children }: JSX.IntrinsicElements["th"]) => (
            <th
              style={{
                padding: "0.8rem 1.2rem",
                textAlign: "center",
                fontSize: "1rem",
                lineHeight: "1.6rem",
                color: theme.colors.gray400,
                fontWeight: theme.font.medium,
                fontFamily: theme.font.family.neue,
              }}
            >
              {children}
            </th>
          ),
          td: ({ children }: JSX.IntrinsicElements["td"]) => (
            <td
              style={{
                padding: "0.8rem 1.2rem",
                textAlign: "center",
                fontSize: "1rem",
                lineHeight: "1.6rem",
                color: theme.colors.gray400,
                fontWeight: theme.font.medium,
                fontFamily: theme.font.family.neue,
              }}
            >
              {children}
            </td>
          ),
          blockquote: ({ children }: JSX.IntrinsicElements["blockquote"]) => (
            <StyledBlockquote>{children}</StyledBlockquote>
          ),
        },
      })
      .processSync(content).result
  }, [content, theme])
}

const StyledBlockquote = styled.blockquote`
  border-left: 2px solid #a7f3d0;
  padding-left: 0.5rem;
  color: #065f46;
  font-style: italic;
`

const CodeBlock = ({ children, className }: JSX.IntrinsicElements["code"]) => {
  const [copied, setCopied] = useState(false)
  const ref = useRef<HTMLElement>(null)
  const { theme } = useThemeContext()

  useEffect(() => {
    if (copied) {
      const interval = setTimeout(() => setCopied(false), 1000)
      return () => clearTimeout(interval)
    }
  }, [copied])

  // Highlight.js adds a `className` so this is a hack to detect if the code block
  // is a language block wrapped in a `pre` tag.
  if (className) {
    return (
      <>
        <code
          ref={ref}
          className={className}
          style={{
            width: "100%",
          }}
        >
          {children}
        </code>
        <ActionIconWrapper
          style={{
            backgroundColor: theme.colors.gray900,
          }}
          onClick={() => {
            if (ref.current) {
              copyToClipboard(ref.current.innerText ?? "")
              dispatchToast({
                title: "Content copied",
                description: { value: ref.current.innerText ?? "" },
                icon: <CopyIcon />,
                type: "success",
                id: ref.current.innerText ?? "",
              })
              setCopied(true)
            }
          }}
          size="L"
        >
          {copied ? (
            <TickIcon height={16} width={16} color={theme.colors.success} />
          ) : (
            <CopyIcon height={16} width={16} color={theme.colors.gray400} />
          )}
        </ActionIconWrapper>
      </>
    )
  }

  return (
    <code
      style={{
        width: "100%",
      }}
      className={className}
    >
      {children}
    </code>
  )
}

const formatMarketStringToTable = (cell: string): JSX.Element => {
  const marketEntries = cell.split("),").map((entry) => entry.trim())
  return (
    <table
      style={{
        borderCollapse: "collapse",
        width: "100%",
      }}
    >
      <tbody>
        {marketEntries.map((entry, index) => {
          const [market, value] = entry.split(" (")
          return (
            <tr key={index}>
              <td
                style={{
                  border: `1px solid ${defaultTheme.colors.gray500}`,
                  padding: "0.5rem",
                  color: defaultTheme.colors.white,
                  textAlign: "left",
                }}
              >
                {market}
              </td>

              <td
                style={{
                  border: `1px solid ${defaultTheme.colors.gray500}`,
                  padding: "0.5rem",
                  color: defaultTheme.colors.white,
                  textAlign: "right",
                }}
              >
                {minifyCurrencyValue((value || "").replace("USD)", ""))}
                {/* {value} */}
              </td>
            </tr>
          )
        })}
      </tbody>
    </table>
  )
}
export const StyledTable = styled.table`
  ${({ theme }) => css`
    width: 100%;
    border-collapse: collapse;
    border-radius: 0.8rem;
    max-width: 60rem;
    overflow: hidden;

    > thead {
      background-color: ${theme.colors.gray800};
      color: ${theme.colors.white};

      > tr {
        border-bottom: 1px solid ${theme.colors.gray700};
      }

      > tr > th {
        padding: 0.8rem 1.2rem;
        text-align: center;
        font-size: 1rem;
        line-height: 1.6rem;
        font-weight: ${theme.font.bold};
        font-family: ${theme.font.family.neue};
        border-right: 1px solid ${theme.colors.gray700};
        &:last-child {
          border-right: none;
        }
      }
    }

    > tbody {
      > tr {
        &:nth-child(even) {
          background-color: ${theme.colors.gray900};
        }
        &:hover {
          background-color: ${theme.colors.gray800};
        }
      }

      > tr > td {
        padding: 0.8rem 1.2rem;
        color: ${theme.colors.gray400};
        text-align: center;
        font-size: 1rem;
        line-height: 1.6rem;
        font-weight: ${theme.font.medium};
        font-family: ${theme.font.family.neue};
        border-right: 1px solid ${theme.colors.gray700};
        &:last-child {
          border-right: none;
        }
      }
    }
  `}
`

export const MARKDOWN_TEST_MESSAGE = `
# Heading level 1

This is the first paragraph.

This is the second paragraph.

This is the third paragraph.

## Heading level 2

This is an [anchor](https://github.com).

### Heading level 3

This is **bold** and _italics_.

#### Heading level 4

This is \`inline\` code.

This is a code block:

\`\`\`tsx
const Message = () => {
  return <div>hi</div>;
};
\`\`\`

##### Heading level 5

###### Heading level 6

This is an unordered list:

- One
- Two
- Three, and **bold**

This is an ordered list:

1. One
1. Two
1. Three

This is a complex list:

1. **Bold**: One
    - One
    - Two
    - Three
  
2. **Bold**: Three
    - One
    - Two
    - Three
  
3. **Bold**: Four
    - One
    - Two
    - Three

###### Heading level 6

> This is a blockquote.

This is a table:

| Vegetable | Description |
|-----------|-------------|
| Carrot    | A crunchy, orange root vegetable that is rich in vitamins and minerals. It is commonly used in soups, salads, and as a snack. |
| Broccoli  | A green vegetable with tightly packed florets that is high in fiber, vitamins, and antioxidants. It can be steamed, boiled, stir-fried, or roasted. |
| Spinach   | A leafy green vegetable that is dense in nutrients like iron, calcium, and vitamins. It can be eaten raw in salads or cooked in various dishes. |
| Bell Pepper | A colorful, sweet vegetable available in different colors such as red, yellow, and green. It is often used in stir-fries, salads, or stuffed recipes. |
| Tomato    | A juicy fruit often used as a vegetable in culinary preparations. It comes in various shapes, sizes, and colors and is used in salads, sauces, and sandwiches. |
| Cucumber   | A cool and refreshing vegetable with a high water content. It is commonly used in salads, sandwiches, or as a crunchy snack. |
| Zucchini | A summer squash with a mild flavor and tender texture. It can be sautéed, grilled, roasted, or used in baking recipes. |
| Cauliflower | A versatile vegetable that can be roasted, steamed, mashed, or used to make gluten-free alternatives like cauliflower rice or pizza crust. |
| Green Beans | Long, slender pods that are low in calories and rich in vitamins. They can be steamed, stir-fried, or used in casseroles and salads. |
| Potato | A starchy vegetable available in various varieties. It can be boiled, baked, mashed, or used in soups, fries, and many other dishes. |

This is a mermaid diagram:

\`\`\`mermaid
gitGraph
    commit
    commit
    branch develop
    checkout develop
    commit
    commit
    checkout main
    merge develop
    commit
    commit
\`\`\`

\`\`\`latex
\\[F(x) = \\int_{a}^{b} f(x) \\, dx\\]
\`\`\`
`
