import { toKebabCase, isUrl, toPlural } from '@vizality/util/string';
import React, { memo, useState, useEffect, Children } from 'react';
import { getModule, contextMenu } from '@vizality/webpack';
import { getMediaDimensions } from '@vizality/util/file';
import { openModal } from '@vizality/modal';
import { joinClassNames } from '@vizality/util/dom';
import { Regexes, Protocols } from '@vizality/constants';
import { existsSync, promises } from 'fs';
import Markdown from 'react-markdown';
import gfm from 'remark-gfm';

import { CodeBlock, Icon, DeferredRender, Spinner, LazyImageZoomable, Modal, ImageModal, Anchor, ContextMenu, Checkbox, Flex } from '.';

const { readFile } = promises;
const { openContextMenu, closeContextMenu } = contextMenu;

export default memo(({ source, type, addonId }) => {
  const [ markdown, setMarkdown ] = useState();
  const { size32, size24, size20, size16, size14, size12 } = getModule('size32');
  const { anchor, anchorUnderlineOnHover } = getModule('anchorUnderlineOnHover');
  const { imageWrapper } = getModule('imageWrapper');

  useEffect(() => {
    const getSource = async () => {
      const md = await readFile(source, 'utf-8');
      // For Vizality Changelog
      setMarkdown(md.replace(/{(fixed|added|improved|progress)( marginTop)?}/g, '').replace(/(# Changelog)/, '').trim());
    };

    if (source) {
      if (existsSync(source)) {
        getSource();
      } else {
        setMarkdown(source.trim());
      }
    }
  }, [ source, markdown ]);

  const flatten = (text, child) => {
    return typeof child === 'string'
      ? text + child
      : Children.toArray(child?.props?.children).reduce(flatten, text);
  };

  const generateId = (() => {
    let index = 0;
    return (prefix = '') => {
      index += 1;
      return `${prefix}-${index}`;
    };
  })();

  const getHeader = (level, children) => {
    const sizes = [ null, size32, size24, size20, size16, size14, size12 ];
    const text = toKebabCase(children.reduce(flatten, ''));
    const slug = `vz-markdown-header--${generateId(text)}`;
    const Header = `h${level}`;

    return (
      <Header id={slug} className={joinClassNames('vz-markdown-header', `vz-markdown-header-h${level}`, sizes[level])}>
        <Anchor className='vz-markdown-anchor' href={`#${slug}`}>
          <Icon
            name='link'
            className='vz-markdown-anchor-icon'
            iconClassName='vz-markdown-anchor-icon'
            size='16px'
            svgOnly
            onContextMenu={evt => openContextMenu(evt, () => (
              <ContextMenu.Menu navId='vz-markdown-link-reference-context-menu' onClose={closeContextMenu}>
                <ContextMenu.Item
                  id='copy-link'
                  label='Copy Link'
                  action={() => {
                    try {
                      const href = vizality.api.routes.getLocation()?.href;
                      const protocolUrl = href?.replace(href?.match(new RegExp(`${Regexes.DISCORD}/`))[0], '');
                      console.log(protocolUrl);
                      DiscordNative?.clipboard?.copy(`<${protocolUrl.replace('vizality', 'vizality:/')}#${slug}>`);
                    } catch (err) {

                    }
                  }}
                />
              </ContextMenu.Menu>
            ))}
          />
        </Anchor>
        {children}
      </Header>
    );
  };

  const renderers = {
    p: ({ children }) => {
      return <p className='vz-markdown-p'>
        {children}
      </p>;
    },

    ul: ({ _ordered, start, children }) => {
      const attrs = {};
      if (start !== null && start !== 1 && start !== undefined) {
        attrs.start = start.toString();
      }
      return (
        <ul
          className='vz-markdown-ul'
          start={attrs.start}
        >
          {children}
        </ul>
      );
    },

    ol: ({ _ordered, start, children }) => {
      const attrs = {};
      if (start !== null && start !== 1 && start !== undefined) {
        attrs.start = start.toString();
      }
      return (
        <ol
          className='vz-markdown-ol'
          start={attrs.start}
        >
          {children}
        </ol>
      );
    },

    input: ({ children, checked, disabled, type }) => (
      type === 'checkbox'
        ? (
          <Checkbox className='vz-markdown-checkbox' align={Flex.Align.TOP} value={!!checked} readOnly={disabled}>
            {children && children}
          </Checkbox>
        )
        : (
          <input type={type} checked={checked} disabled={disabled} />
        )
    ),

    li: ({ children, className }) => (
      <li className={joinClassNames('vz-markdown-li', className)}>
        {children}
      </li>
    ),

    a: ({ href, children }) => (
      <Anchor
        href={href}
        className={joinClassNames('vz-markdown-link', anchor, anchorUnderlineOnHover)}
      >
        {children}
      </Anchor>
    ),

    em: ({ children }) => (
      <em className='vz-markdown-em'>
        {children}
      </em>
    ),

    strong: ({ children }) => (
      <strong className='vz-markdown-strong'>
        {children}
      </strong>
    ),

    blockquote: ({ children }) => (
      <blockquote className='vz-markdown-blockquote'>
        {children}
      </blockquote>
    ),

    hr: () => <hr className='vz-markdown-hr' />,

    code: ({ inline, className, children }) => {
      const language = className?.match(/(language-)/)?.input?.replace('language-', '');
      return (
        inline
          ? (
            <code className='vz-markdown-code-inline'>
              {children}
            </code>
          )
          : (
            <CodeBlock
              language={language}
              content={children?.join()}
              className='vz-markdown-code'
            />
          )
      );
    },

    img: ({ alt, src }) => {
      const [ _src, setSrc ] = useState(src);
      const [ size, setSize ] = useState({});

      useEffect(async () => {
        async function getSize (src) {
          const dimensions = await getMediaDimensions(src);
          setSize(dimensions);
        }
        let protocolSrc;
        async function getSrc () {
          if (!isUrl(src)) {
            if (addonId) {
              protocolSrc = `${Protocols[toPlural(type).toUpperCase()]}/${addonId}/${src}`;
              setSrc(`${Protocols[toPlural(type).toUpperCase()]}/${addonId}/${src}`);
              await getSize(protocolSrc);
            }
          } else {
            setSrc(src);
          }
        }
        await getSrc();
        await getSize(src);
      }, []);

      return (
        <LazyImageZoomable
          className={joinClassNames('vz-markdown-image', imageWrapper)}
          src={_src}
          alt={alt}
          width={size.width}
          height={size.height}
          onClick={evt => {
            evt.preventDefault();
            openModal(() => props => (
              <Modal.Root {...props} size='dynamic'>
                <ImageModal
                  src={_src}
                  width={size.width}
                  height={size.height}
                />
              </Modal.Root>
            ));
          }}
        />
      );
    },

    table: ({ children }) => {
      return <table className='vz-markdown-table'>{children}</table>;
    },

    thead: ({ children }) => {
      return <thead className='vz-markdown-thead'>{children}</thead>;
    },

    tbody: ({ children }) => {
      return <tbody className='vz-markdown-tbody'>{children}</tbody>;
    },

    tr: ({ children }) => {
      return <tr className='vz-markdown-tr'>{children}</tr>;
    },

    td: ({ style, children }) => {
      return (
        <td className='vz-markdown-td' style={style}>
          {children}
        </td>
      );
    },

    th: ({ style, children }) => {
      return (
        <td className='vz-markdown-th' style={style}>
          {children}
        </td>
      );
    },

    h1: ({ level, children }) => getHeader(level, children),
    h2: ({ level, children }) => getHeader(level, children),
    h3: ({ level, children }) => getHeader(level, children),
    h4: ({ level, children }) => getHeader(level, children),
    h5: ({ level, children }) => getHeader(level, children),
    h6: ({ level, children }) => getHeader(level, children)
  };

  return (
    <>
      {existsSync(source)
        ? (
          <DeferredRender idleTimeout={2500} fallback={
            <div className='vz-markdown-content-loading'>
              <Spinner />
            </div>
          }>
            <Markdown children={markdown} remarkPlugins={[ gfm ]} components={renderers} />
          </DeferredRender>
        )
        : <Markdown children={markdown} remarkPlugins={[ gfm ]} components={renderers} />
      }
    </>
  );
});
