/* eslint-disable react/no-danger */
import { useState } from 'react';
import Copy from 'copy-to-clipboard';
import { type SxProps } from '@mui/material';

import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';
import typescript from 'highlight.js/lib/languages/typescript';
import json from 'highlight.js/lib/languages/json';
import elixir from 'highlight.js/lib/languages/elixir';
import java from 'highlight.js/lib/languages/java';
import swift from 'highlight.js/lib/languages/swift';
import objectivec from 'highlight.js/lib/languages/objectivec';
import kotlin from 'highlight.js/lib/languages/kotlin';
import protobuf from 'highlight.js/lib/languages/protobuf';
import python from 'highlight.js/lib/languages/python';
import yaml from 'highlight.js/lib/languages/yaml';
import shell from 'highlight.js/lib/languages/shell';
import diff from 'highlight.js/lib/languages/diff';
import plaintext from 'highlight.js/lib/languages/plaintext';
import markdown from 'highlight.js/lib/languages/markdown';

import 'highlight.js/styles/atom-one-dark.css';

import { FileCopy as CopyIcon, Check as CheckIcon } from '@mui/icons-material';
import colors from '../Colors';
import { styled } from '../Theme';
import LightBox from './LightBox';

hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('js', javascript);
hljs.registerLanguage('jsx', javascript);
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('ts', typescript);
hljs.registerLanguage('tsx', typescript);
hljs.registerLanguage('json', json);
hljs.registerLanguage('elixir', elixir);
hljs.registerLanguage('java', java);
hljs.registerLanguage('kotlin', kotlin);
hljs.registerLanguage('kt', kotlin);
hljs.registerLanguage('protobuf', protobuf);
hljs.registerLanguage('protobuffer', protobuf);
hljs.registerLanguage('python', python);
hljs.registerLanguage('py', python);
hljs.registerLanguage('yaml', yaml);
hljs.registerLanguage('yml', yaml);
hljs.registerLanguage('shell', shell);
hljs.registerLanguage('sh', shell);
hljs.registerLanguage('plaintext', plaintext);
hljs.registerLanguage('text', plaintext);
hljs.registerLanguage('patch', diff);
hljs.registerLanguage('diff', diff);
hljs.registerLanguage('swift', swift);
hljs.registerLanguage('objectivec', objectivec);
hljs.registerLanguage('oc', objectivec);
hljs.registerLanguage('markdown', markdown);

export interface CodeBlockProps extends React.HTMLAttributes<HTMLPreElement> {
  code?: string;
  language?: string;
  children?: React.ReactNode;
  dark?: true | false;
  showDefaultText?: true | false;
  sx?: SxProps;
}

export default function CodeBlock({
  code = '',
  language = 'text',
  children = null,
  dark = true,
  showDefaultText = true,
  ...rest
}: CodeBlockProps) {
  const [copied, setCopied] = useState(false);
  const source = code || (children as string);

  const onCopy = () => {
    if (copied) {
      return;
    }
    Copy(source);
    setCopied(true);
    // 恢复 copied 状态
    setTimeout(() => {
      setCopied(false);
    }, 1500);
  };

  const originEl = (
    <Pre {...rest}>
      <span className="codeblock__inner">
        <code
          className={`hljs ${language}`}
          dangerouslySetInnerHTML={{ __html: hljs.highlightAuto(source, [language]).value }}
        />
      </span>
      <div className="copy-button" onClick={onCopy}>
        {showDefaultText && <span className="default-text">{language}</span>}
        <span className="hover-text">
          {!copied ? (
            <>
              <CopyIcon className="copy-icon" />
              <span className="button-lang-text">copy</span>
            </>
          ) : (
            <>
              <CheckIcon className="copy-icon" />
              <span className="button-lang-text">copied</span>
            </>
          )}
        </span>
      </div>
    </Pre>
  );

  if (!dark) {
    return <LightBox>{originEl}</LightBox>;
  }

  return originEl;
}

const fontFamily = 'source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace !important';
const Pre = styled('pre', {
  shouldForwardProp: (prop) => prop !== 'sx',
})`
  font-family: ${fontFamily};
  display: block;
  font-size: 14px;
  line-height: 1.42857143;
  margin: 0 0 32px;
  word-break: break-word;
  word-wrap: break-word;
  text-align: left;
  border-radius: 5px;
  border: 1px solid;
  border-color: ${({ theme }) => theme.palette.divider};
  background: ${({ theme }) => theme.palette.background.default};
  color: ${({ theme }) => theme.palette.common.white};
  position: relative;

  .codeblock__inner {
    display: block;
    width: 100%;
    max-height: 46.25rem;
    overflow: auto;
  }

  .copy-button {
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    top: 8px;
    right: 24px;
    padding: 4px;
    min-width: 72px;
    height: 24px;
    border-radius: 4px;
    background: transparent;
    cursor: pointer;
    transition: all ease 0.2s;

    .copy-icon {
      font-size: 16px;
      color: ${colors.common.white};
    }

    .hover-text {
      display: none;
    }
    .default-text {
      font-size: 12px;
      color: ${colors.common.white};
    }
  }

  &:hover {
    .copy-button {
      display: flex;
      background: ${colors.primary.main};

      .hover-text {
        display: flex;
        justify-content: center;
        align-items: center;
        color: ${colors.common.white};
      }
      .default-text {
        display: none;
        font-size: 12px;
      }
    }
  }

  .button-lang-text {
    margin-left: 4px;
    font-size: 12px;
    color: ${colors.common.white};
  }

  code {
    counter-reset: line;
    counter-increment: line;
    padding: 16px;
    font-family: ${fontFamily};
    color: ${colors.common.white};
    * {
      font-family: ${fontFamily};
    }
  }

  .hljs * {
    white-space: normal; // autowrap
  }
`;
