import React, { useMemo, useRef, useEffect, useState, ChangeEvent, FormEvent } from "react";
import { Message, useChat } from "ai/react";
import { useAppDispatch, useAppSelector } from "../../store/hook";
import {
  compiler_state,
  initEsbuild,
} from "../../store/features/compilerSlice";
import { editor_state, set_monaco_input_value, update_editor_code } from "../../store/features/editorSlice";
import { theme_state } from "../../store/features/themeSlice";
import { ModalEnum, open_modal } from "../../store/features/modalSlice";

import ConsoleLog from "./ConsoleLog";
import Iframe from "./Iframe";
import InputCodeTab from "./InputCodeTab";
import Footer from "./Footer";
import Pane from "./Pane";
import { SendIcon } from "../../constants/icon";
import ReactMarkdown from "react-markdown";
import { ChatRequestOptions } from "ai";

const DEFAULT_PROMPT = "You are a helpful AI assistant. Assist the user in writing React code, ensuring that all logic is contained within a single app component file and that the output is rendered to the DOM's root element. Always write React using inline styles and end the code snippet with rendering to root: ReactDOM.render(<App />, document.getElementById('root'));"

const Playground = () => {
  const dispatch = useAppDispatch();
  const formRef = useRef<HTMLFormElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);
  const { theme } = useAppSelector(theme_state);
  const { esbuildStatus, isCompiling, output } = useAppSelector(compiler_state);
  const { logs, editorValue, isLogTabOpen } = useAppSelector(editor_state);
  const [markdownCode, setMarkdownCode] = useState('');
  const [prevMarkdownCode, setPrevMarkdownCode] = useState(markdownCode);
  const [isSystemInputVisible, setSystemInputVisible] = useState(false);
  const [isModelInputVisible, setModelInputVisible] = useState(false);
  const [selectedModel, setSelectedModel] = useState("GPT-4");

  const [systemMessage, setSystemMessage] = useState(
    DEFAULT_PROMPT
  );

  const isValidCodeBlock = (markdownCode: string) => {
    return markdownCode && markdownCode.length > 10 && markdownCode.includes('\n');
  }

  useEffect(() => {
    const timer = setInterval(() => {
      if (isValidCodeBlock(markdownCode) && markdownCode !== prevMarkdownCode) {
        dispatch(update_editor_code({ type: 'javascript', content: markdownCode }));
        setPrevMarkdownCode(markdownCode);
      }
    }, 2000);

    return () => {
      clearInterval(timer);
    };
  }, [markdownCode, prevMarkdownCode, dispatch]);

  const { append, messages, input, setInput, handleSubmit, ...rest } = useChat({
    onError: (error) => {
        console.error(error);
    },
  });

  const modifiedHandleSubmit = async (e: React.FormEvent<HTMLFormElement>, chatRequestOptions?: ChatRequestOptions) => {
    e.preventDefault();

    if (messages.length === 0 || messages[0].role !== "system") {
        const systemMessageToAdd: Message = {
            id: `${Date.now()}-system`,
            createdAt: new Date(),
            content: systemMessage,
            role: "system"
        };

        await append(systemMessageToAdd);
    } else {
      handleSubmit(e, chatRequestOptions);
    }
  };

  useEffect(() => {
    if (!esbuildStatus.isReady) {
      dispatch(initEsbuild());
    }
  }, [dispatch, esbuildStatus]);

  useEffect(() => {
    dispatch(open_modal(ModalEnum.TEMPLATE));
  }, [dispatch]);

  useEffect(() => {
    if(isValidCodeBlock(markdownCode)) {
      const newEditorValue = {
        name: "React",
        description: "By codetree",
        public: true,
        iconSrc: "/icons/reactjs.svg",
        tabs: {
          javascript: {
            title: "JS/JSX",
            entryPoints: "index.js",
            monacoLanguage: "javascript",
            data: markdownCode
          },
          html: {
            title: "index.html",
            entryPoints: "index.html",
            monacoLanguage: "html",
            data: ""
          },
          css: {
            title: "main.css",
            entryPoints: "main.css",
            monacoLanguage: "css",
            data: ""
          }
        }
      };
      
      dispatch(set_monaco_input_value(newEditorValue as any));
    }
  }, [markdownCode, dispatch]);

  return (
    <div style={{ background: theme.background }}>
      <div className="flex flex-col">
          <div className="px-4 pb-2 pt-3 shadow-lg sm:pb-3 sm:pt-4">
            <span className="text-lg font-semibold">System Prompt</span>
            <button
              onClick={() => setSystemInputVisible(!isSystemInputVisible)}
              className="p-2 rounded-full hover:bg-gray-200 transition duration-300"
            >
              {isSystemInputVisible ? '−' : '+'}
            </button>
            <span className="text-lg font-semibold">Model</span>
              <button
                onClick={() => setModelInputVisible(!isModelInputVisible)}
                className="p-2 rounded-full hover:bg-gray-200 transition duration-300"
              >
                {isModelInputVisible ? '−' : '+'}
            </button>
            
            {isSystemInputVisible && (
              <textarea 
                className="textarea my-4 border p-2 rounded-md shadow-sm w-full resize-none" 
                value={systemMessage || ''} 
                onChange={e => setSystemMessage(e.target.value)} 
                placeholder="Enter custom system prompt here"
                rows={2} // initial number of rows, can be adjusted
                style={{ overflowWrap: 'break-word', whiteSpace: 'pre-wrap' }}
              />
            )}
            {isModelInputVisible && (
              <div className="my-4">
                <select 
                  value={selectedModel}
                  onChange={(e) => setSelectedModel(e.target.value)}
                  className="border p-2 rounded-md shadow-sm w-full bg-transparent text-gray-700"
                  >
                  <option value="GPT-4">GPT-4</option>
                </select>
              </div>
            )}

            <form
              ref={formRef}
              onSubmit={modifiedHandleSubmit}
              className="relative w-full"
            >

            <textarea ref={inputRef} onChange={(e) => setInput(e.target.value)}
              placeholder="Create a twitter clone"
              onKeyDown={(e) => {
                if (e.key === "Enter" && !e.shiftKey) {
                  formRef.current?.requestSubmit();
                  e.preventDefault();
                }
              }}
              spellCheck={false} className="textarea" value={input} />
            <button 
                type="submit"
                className="absolute inset-y-0 right-3 my-auto flex h-8 w-8 items-center justify-center rounded-md transition-all"
            >
              <SendIcon
                  className={"h-4 w-4"}
              />
            </button>
          </form>
        </div>
        <div className="flex flex-col items-start space-y-4 overflow-y-auto max-h-[20vh]">
          {messages?.map((message, index) => (
            <p key={index} className="messages-text">
              <ReactMarkdown
                className="prose mt-1 w-full break-words prose-p:leading-relaxed" 
                components={{
                  a: (props) => (
                    <a {...props} target="_blank" rel="noopener noreferrer" />
                  ),
                  // @ts-ignore
                  code: ({node, ...props}) => {
                    const codeValue = props.children[0] || '';
                    setMarkdownCode(codeValue as any);
                    
                    return null;
                  }                    
                }}
              >
                {message.content}
              </ReactMarkdown>
            </p>
          ))}
        </div>
      </div>
      <Pane
        panelA={<InputCodeTab editorValue={editorValue} />}
        panelB={
          <Iframe
            tabs={editorValue.tabs}
            output={output}
            isCompiling={isCompiling}
            esbuildStatus={esbuildStatus}
          />
        }
        panelC={<ConsoleLog logs={logs} />}
        lastPanelVisibility={isLogTabOpen}
      />

      <Footer />
    </div>

  );
};

export default Playground;
