import Head from "next/head";
import Image from "next/image";
import { Inter } from "@next/font/google";
import styles from "@/styles/Home.module.css";
import {
  QueryClientProvider,
  QueryClient,
  useQuery,
  useMutation,
} from "@tanstack/react-query";
import { useCallback, useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";
import { useKey } from "react-use";
import cn from "clsx";
// import rehypePrettyCode from "rehype-pretty-code";
// import * as shiki from "shiki";
import remarkGfm from "remark-gfm";
import styled from "styled-components";
// import rehypeHighlight from "rehype-highlight";

// @ts-ignore
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
// @ts-ignore
import theme from "react-syntax-highlighter/dist/cjs/styles/prism/vs-dark";
// const CDN_BASE = "https://npm.elemecdn.com/";
// shiki.setCDN(`${CDN_BASE}/shiki@0.14.1/`);

const SMD = styled(ReactMarkdown)`
  blockquote,
  hr,
  p {
    margin-block: 1rem;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    font-weight: bold;
    margin-block: 1rem;
  }
  h1 {
    font-size: 3rem;
    color: var(--h1-color);
  }
  h2 {
    font-size: 2.5rem;
    color: var(--h2-color);
  }
  h3 {
    font-size: 2rem;
    color: var(--h3-color);
  }
  h4 {
    font-size: 1.5rem;
    color: var(--h4-color);
  }
  h5 {
    font-size: 1rem;
    color: var(--h5-color);
  }
  h6 {
    font-size: 0.9rem;
    color: var(--h6-color);
  }

  img {
    display: block;
    margin-left: auto;
    margin-right: auto;
  }

  .img-alt {
    display: block;
    margin: 0 0 1rem 0;
    font-size: 16px;
    text-align: center;
  }

  th {
    font-weight: 600;
  }

  thead {
    border-bottom: 2px solid var(--background-modifier-border);
  }

  tr {
    line-height: normal;
    padding: 0 4px;
    background-color: var(--pre-code);
  }

  tr:nth-child(0) {
    padding-top: 4px;
  }

  th,
  td {
    padding: 0.5em 1em;
  }

  td {
    border-bottom: 1px solid var(--background-modifier-border);
  }
  td:not(:last-child) {
    border-right: 1px solid var(--background-modifier-border);
  }

  strong {
    font-weight: 600;
  }

  a {
    color: var(--text-a);
    text-decoration: none;
  }

  a:hover {
    color: var(--text-a-hover);
    text-decoration: none;
  }

  blockquote {
    margin: 1rem 0;
    padding-inline: 2ch;
    padding-block: 0.5rem;
    background-color: var(--pre-code);
    border-left: 0.5ch solid var(--interactive-accent);
  }
  blockquote:has(> blockquote) {
    padding-bottom: 0;
  }
  blockquote > blockquote {
    margin-bottom: 0;
  }
  blockquote:not(:has(> blockquote)) {
    margin-bottom: 1rem;
  }

  hr {
    background-color: var(--background-modifier-border);
    height: 1px;
    border: 0;
  }

  ul {
    list-style-type: revert;
  }
  ol {
    list-style-type: decimal;
  }
  ul:not(.contains-task-list),
  ol:not(.contains-task-list) {
    padding-left: 2em;
  }
  ul.contains-task-list,
  ol.contains-task-list {
    margin-left: 0;
    list-style-type: none;
  }
`;

const TextTyper = ({
  // now the phrase, interval and HTML element desired will come via props and we have some default values here
  text = "",
  skip = 0,
  onFinish = () => {},
}) => {
  const [typedText, setTypedText] = useState("");
  const interval = 50;
  const step = Math.ceil(text.length / 100);

  // @ts-ignore
  const typingRender = (text, updater, interval) => {
    let localTypingIndex = skip;
    let localTyping = text.slice(0, skip);
    if (text) {
      let printer = setInterval(() => {
        if (localTypingIndex < text.length) {
          updater(
            (localTyping += text.slice(
              localTypingIndex,
              localTypingIndex + step
            ))
          );
          localTypingIndex += step;
          document.querySelector("#anchor")?.scrollIntoView();
        } else {
          localTypingIndex = 0;
          localTyping = "";
          clearInterval(printer);
          onFinish();
        }
      }, interval);
    }
  };
  useEffect(() => {
    typingRender(text, setTypedText, interval);
  }, [text, interval]);

  return <div className="whitespace-pre-wrap">{typedText}</div>;
};

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
    },
  },
});
const p429 = "请求过多，请稍后再试";

const rehypePlugins: any[] = [
  // [
  //   rehypePrettyCode,
  //   {
  //     // theme: {
  //     //   dark: "github-dark",
  //     //   light: "github-light",
  //     // },
  //     theme: "github-dark",
  //     keepBackground: true,
  //   },
  // ],
  // rehypeHighlight,
];
const remarkPlugins = [remarkGfm];
const components = {
  // @ts-ignore
  code({ node, inline, className, children, ...props }: any) {
    const match = /language-(\w+)/.exec(className || "");
    return !inline && match ? (
      <SyntaxHighlighter
        style={theme}
        language={match[1] || "clike"}
        PreTag="div"
        {...props}
      >
        {String(children).replace(/\n$/, "")}
      </SyntaxHighlighter>
    ) : (
      <code className={className} {...props}>
        {children}
      </code>
    );
  },
};
const Markdown = ({ children }: { children: string }) => {
  return (
    <SMD
      rehypePlugins={rehypePlugins}
      remarkPlugins={remarkPlugins}
      // components={components}
    >
      {children}
    </SMD>
  );
};

const ask = async (
  query: string,
  context: [string, string][],
  jwt?: string
) => {
  const res = await fetch(`/ai/ask`, {
    method: "POST",
    headers: {
      Authorization: jwt,
    } as Record<string, string>,
    body: JSON.stringify({
      query: query,
      context: context
        .slice(1)
        .filter((c) => c[1] !== p429)
        .map((c) => ({ query: c[0], answer: c[1] })),
    }),
  });
  if (res.status === 429) {
    return { code: 1, msg: p429 };
  }
  return (await res.json()) as { code: number; msg: string };
};

let contexts = [
  [
    "hello",
    "您好!我是小Vaala。我可以回答您的问题、写文章、写作业、翻译，对于一些法律等领域的问题我也可以给你提供信息。",
  ],
];

function Home() {
  const [query, setQuery] = useState("");
  const [question, setQuestion] = useState("");
  const [answer, setAnswer] = useState("");
  const [context, setContext] = useState<[string, string][]>(
    contexts as [string, string][]
  );
  const { data: jwt } = useQuery(["jwt"], async () => {
    const resp = await fetch(`/auth/jwt`);
    const res = (await resp.json())["authorization"];
    return res;
  });
  useKey("Enter", (e) => {
    if (!e.shiftKey && !e.metaKey && !e.ctrlKey && !e.isComposing) {
      e.preventDefault();
      handleAsk.mutate();
    }
  });
  const handleTypeFinish = useCallback(() => {
    setQuestion("");
    setContext((v) => [...v, [question, answer]]);
    setAnswer("");
    document.querySelector("#anchor")?.scrollIntoView();
  }, [query, answer]);

  const handleAsk = useMutation(async () => {
    if (query.length === 0) return;
    setQuestion(query);
    setQuery("");

    setTimeout(() => document.querySelector("#anchor")?.scrollIntoView(), 100);
    (async () => {
      const ans = await ask(query, context, jwt || "").finally(() => {
        setTimeout(
          () => document.querySelector("#anchor")?.scrollIntoView(),
          300
        );
      });
      if (ans.code) alert(ans.msg);
      else setAnswer(ans.msg);
    })();
  });
  return (
    <>
      <Head>
        <title>Vaala Chat</title>
        <meta name="description" content="Chat to small vaala" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
        <style
          dangerouslySetInnerHTML={{
            __html: `.lds-dual-ring {
  display: inline-block;
  width: 18px;
  height: 18px;
}
.lds-dual-ring:after {
  content: " ";
  display: block;
  width: 18px;
  height: 18px;
  margin: 0 0 0 8px;
  border-radius: 50%;
  border: 2px solid #fff;
  border-color: #fff transparent #fff transparent;
  animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
pre > code {
  white-space: pre-wrap;
}
`,
          }}
        />
      </Head>
      <main
        className={cn(styles.main, "px-2 md:px-16 xl:px-64")}
        style={{ width: "100dvw", height: "100dvh" }}
      >
        <div style={{ maxHeight: "calc(100dvh - 75px)", overflowY: "auto" }}>
          {context?.map((qa, i) => {
            return (
              <div
                key={i}
                className="flex w-full flex-col items-stretch justify-start"
              >
                <div className="p-4 w-full bg-white dark:bg-slate-800">
                  <Markdown>{`ME: ${qa[0]}`}</Markdown>
                </div>
                <div className="p-4 w-full bg-slate-100  dark:bg-slate-700">
                  {qa[1] === p429 ? (
                    <span className="text-red-500">{qa[1]}</span>
                  ) : (
                    <Markdown
                    // remarkPlugins={[remarkGfm]}
                    >
                      {`🐳: ${qa[1]}`}
                    </Markdown>
                  )}
                </div>
              </div>
            );
          })}
          {question || answer ? (
            <>
              <div className="p-4 w-full bg-white dark:bg-slate-800">
                <Markdown>{`ME: ${question}`}</Markdown>
              </div>
              <div
                className="p-4 w-full flex items-center bg-slate-100  dark:bg-slate-700"
                style={{ lineHeight: "18px" }}
              >
                {answer ? (
                  <TextTyper
                    skip={3}
                    text={`🐳: ${answer}`}
                    onFinish={handleTypeFinish}
                  />
                ) : (
                  <>
                    <div>{`🐳: 思考中`}</div>
                    <svg
                      // xmlns="http://www.w3.org/2000/svg"
                      // xmlns:xlink="http://www.w3.org/1999/xlink"
                      // style="margin: auto; background: rgb(241, 242, 243); display: block;"
                      width="18px"
                      height="18px"
                      viewBox="0 0 100 100"
                      preserveAspectRatio="xMidYMid"
                    >
                      <circle cx="27.5" cy="57.5" r="5" fill="#85a2b6">
                        <animate
                          attributeName="cy"
                          calcMode="spline"
                          keySplines="0 0.5 0.5 1;0.5 0 1 0.5;0.5 0.5 0.5 0.5"
                          repeatCount="indefinite"
                          values="57.5;42.5;57.5;57.5"
                          keyTimes="0;0.3;0.6;1"
                          dur="1s"
                          begin="-0.6s"
                        ></animate>
                      </circle>{" "}
                      <circle cx="42.5" cy="57.5" r="5" fill="#bbcedd">
                        <animate
                          attributeName="cy"
                          calcMode="spline"
                          keySplines="0 0.5 0.5 1;0.5 0 1 0.5;0.5 0.5 0.5 0.5"
                          repeatCount="indefinite"
                          values="57.5;42.5;57.5;57.5"
                          keyTimes="0;0.3;0.6;1"
                          dur="1s"
                          begin="-0.44999999999999996s"
                        ></animate>
                      </circle>{" "}
                      <circle cx="57.5" cy="57.5" r="5" fill="#dce4eb">
                        <animate
                          attributeName="cy"
                          calcMode="spline"
                          keySplines="0 0.5 0.5 1;0.5 0 1 0.5;0.5 0.5 0.5 0.5"
                          repeatCount="indefinite"
                          values="57.5;42.5;57.5;57.5"
                          keyTimes="0;0.3;0.6;1"
                          dur="1s"
                          begin="-0.3s"
                        ></animate>
                      </circle>{" "}
                      <circle cx="72.5" cy="57.5" r="5" fill="#fdfdfd">
                        <animate
                          attributeName="cy"
                          calcMode="spline"
                          keySplines="0 0.5 0.5 1;0.5 0 1 0.5;0.5 0.5 0.5 0.5"
                          repeatCount="indefinite"
                          values="57.5;42.5;57.5;57.5"
                          keyTimes="0;0.3;0.6;1"
                          dur="1s"
                          begin="-0.15s"
                        ></animate>
                      </circle>
                    </svg>
                  </>
                )}
              </div>
            </>
          ) : null}
          <div id="anchor" className="w-full h-1"></div>
        </div>
        <div
          className="fixed bottom-0 p-4 w-full flex justify-center items-center bg-gray-50 dark:bg-gray-900 border-t border-solid border-sky-400"
          style={{ width: "100vw" }}
        >
          <textarea
            rows={1}
            className="flex-1 border-sky-500 border border-solid focus:border-sky-300 focus:outline-none rounded p-2"
            placeholder="..."
            value={query}
            onChange={(e) => setQuery(e.target.value)}
          ></textarea>
          <button
            type="submit"
            className="text-white bg-sky-500 border-sky-500 border border-solid rounded h-12 p-2 ml-4 hover:border-sky-300 hover:bg-sky-300"
            onClick={() => handleAsk.mutate()}
            onSubmit={() => handleAsk.mutate()}
          >
            Send
          </button>
          <button
            className="text-white bg-sky-500 border-sky-500 border border-solid rounded h-12 p-2 ml-4 hover:border-sky-300 hover:bg-sky-300"
            onClick={() => setContext(contexts as any)}
          >
            Clear
          </button>
        </div>
      </main>
    </>
  );
}

export default function Wrapper() {
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  );
}
