import styled from "styled-components";
import { defaultBorderRadius, Loader } from "../components";
import ContinueButton from "../components/ContinueButton";
import { useCallback, useEffect, useRef, useState } from "react";
import { History } from "../../../schema/History";
import { HistoryNode } from "../../../schema/HistoryNode";
import StepContainer from "../components/StepContainer";
import useContinueGUIProtocol from "../hooks/useWebsocket";
import {
  BookOpen,
  ChatBubbleOvalLeftEllipsis,
  Trash,
} from "@styled-icons/heroicons-outline";
import ComboBox from "../components/ComboBox";
import TextDialog from "../components/TextDialog";
import HeaderButtonWithText from "../components/HeaderButtonWithText";
import ReactSwitch from "react-switch";
import { usePostHog } from "posthog-js/react";
import { useSelector } from "react-redux";
import { RootStore } from "../redux/store";
import LoadingCover from "../components/LoadingCover";

const TopGUIDiv = styled.div`
  overflow: hidden;
`;

const UserInputQueueItem = styled.div`
  border-radius: ${defaultBorderRadius};
  color: gray;
  padding: 8px;
  margin: 8px;
  text-align: center;
`;

const Footer = styled.footer<{ dataSwitchChecked: boolean }>`
  display: flex;
  flex-direction: row;
  gap: 8px;
  justify-content: right;
  padding: 8px;
  align-items: center;
  margin-top: 8px;
  border-top: 0.1px solid gray;
  background-color: ${(props) =>
    props.dataSwitchChecked ? "#12887a33" : "transparent"};
`;

interface GUIProps {
  firstObservation?: any;
}

function GUI(props: GUIProps) {
  const posthog = usePostHog();
  const vscMachineId = useSelector(
    (state: RootStore) => state.config.vscMachineId
  );

  const [usingFastModel, setUsingFastModel] = useState(false);
  const [waitingForSteps, setWaitingForSteps] = useState(false);
  const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
  const [availableSlashCommands, setAvailableSlashCommands] = useState<
    { name: string; description: string }[]
  >([]);
  const [dataSwitchChecked, setDataSwitchChecked] = useState(false);
  const [showDataSharingInfo, setShowDataSharingInfo] = useState(false);
  const [stepsOpen, setStepsOpen] = useState<boolean[]>([]);
  const [history, setHistory] = useState<History | undefined>();
  //   {
  //   timeline: [
  //     {
  //       step: {
  //         name: "Waiting for user input",
  //         cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",
  //         description:
  //           "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and ```\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3",
  //       },
  //       observation: {
  //         title: "ERROR FOUND",
  //         error:
  //           "Traceback (most recent call last):\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n    print(sum(first, second))\n          ^^^^^^^^^^^^^^^^^^\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
  //       },
  //       output: [
  //         {
  //           traceback: {
  //             frames: [
  //               {
  //                 filepath:
  //                   "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
  //                 lineno: 7,
  //                 function: "<module>",
  //                 code: "print(sum(first, second))",
  //               },
  //             ],
  //             message: "unsupported operand type(s) for +: 'int' and 'str'",
  //             error_type:
  //               '          ^^^^^^^^^^^^^^^^^^\n  File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError',
  //             full_traceback:
  //               "Traceback (most recent call last):\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n    print(sum(first, second))\n          ^^^^^^^^^^^^^^^^^^\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
  //           },
  //         },
  //         null,
  //       ],
  //     },
  //     {
  //       step: {
  //         name: "EditCodeStep",
  //         range_in_files: [
  //           {
  //             filepath:
  //               "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
  //             range: {
  //               start: {
  //                 line: 0,
  //                 character: 0,
  //               },
  //               end: {
  //                 line: 6,
  //                 character: 25,
  //               },
  //             },
  //           },
  //         ],
  //         prompt:
  //           "I ran into this problem with my Python code:\n\n                Traceback (most recent call last):\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n    print(sum(first, second))\n          ^^^^^^^^^^^^^^^^^^\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'\n\n                Below are the files that might need to be fixed:\n\n                {code}\n\n                This is what the code should be in order to avoid the problem:\n",
  //         description:
  //           "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and\n```python\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3",
  //       },
  //       output: [
  //         null,
  //         {
  //           reversible: true,
  //           actions: [
  //             {
  //               reversible: true,
  //               filesystem: {},
  //               filepath:
  //                 "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
  //               range: {
  //                 start: {
  //                   line: 0,
  //                   character: 0,
  //                 },
  //                 end: {
  //                   line: 6,
  //                   character: 25,
  //                 },
  //               },
  //               replacement:
  //                 "\nfrom sum import sum\n\nfirst = 1\nsecond = 2\n\nprint(sum(first, second))",
  //             },
  //           ],
  //         },
  //       ],
  //     },
  //     {
  //       active: false,
  //       step: {
  //         name: "SolveTracebackStep",
  //         traceback: {
  //           frames: [
  //             {
  //               filepath:
  //                 "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
  //               lineno: 7,
  //               function: "<module>",
  //               code: "print(sum(first, second))",
  //             },
  //           ],
  //           message: "unsupported operand type(s) for +: 'int' and 'str'",
  //           error_type:
  //             '          ^^^^^^^^^^^^^^^^^^\n  File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError',
  //           full_traceback:
  //             "Traceback (most recent call last):\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n    print(sum(first, second))\n          ^^^^^^^^^^^^^^^^^^\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
  //         },
  //         description: "Running step: SolveTracebackStep",
  //       },
  //       output: [null, null],
  //     },
  //     {
  //       step: {
  //         name: "RunCodeStep",
  //         cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",
  //         description:
  //           "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`",
  //       },
  //       output: [null, null],
  //     },
  //   ],
  //   current_index: 3,
  // } as any);

  const [showFeedbackDialog, setShowFeedbackDialog] = useState(false);

  const topGuiDivRef = useRef<HTMLDivElement>(null);
  const client = useContinueGUIProtocol();

  const [scrollTimeout, setScrollTimeout] = useState<NodeJS.Timeout | null>(
    null
  );
  const scrollToBottom = useCallback(() => {
    if (scrollTimeout) {
      clearTimeout(scrollTimeout);
    }
    // Debounced smooth scroll to bottom of screen
    if (topGuiDivRef.current) {
      const timeout = setTimeout(() => {
        window.scrollTo({
          top: topGuiDivRef.current!.offsetHeight,
          behavior: "smooth",
        });
      }, 200);
      setScrollTimeout(timeout);
    }
  }, [topGuiDivRef.current, scrollTimeout]);

  useEffect(() => {
    const listener = (e: any) => {
      // Cmd + J to toggle fast model
      if (e.key === "i" && e.metaKey && e.shiftKey) {
        setUsingFastModel((prev) => !prev);
      }
    };
    window.addEventListener("keydown", listener);

    return () => {
      window.removeEventListener("keydown", listener);
    };
  }, []);

  useEffect(() => {
    console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate);
    client?.onStateUpdate((state) => {
      // Scroll only if user is at very bottom of the window.
      setUsingFastModel(state.default_model === "gpt-3.5-turbo");
      const shouldScrollToBottom =
        topGuiDivRef.current &&
        topGuiDivRef.current?.offsetHeight - window.scrollY < 100;
      setWaitingForSteps(state.active);
      setHistory(state.history);
      setUserInputQueue(state.user_input_queue);
      setStepsOpen((prev) => {
        const nextStepsOpen = [...prev];
        for (
          let i = nextStepsOpen.length;
          i < state.history.timeline.length;
          i++
        ) {
          nextStepsOpen.push(true);
        }
        return nextStepsOpen;
      });

      if (shouldScrollToBottom) {
        scrollToBottom();
      }
    });
    client?.onAvailableSlashCommands((commands) => {
      console.log("Received available slash commands: ", commands);
      setAvailableSlashCommands(
        commands.map((c) => {
          return {
            name: "/" + c.name,
            description: c.description,
          };
        })
      );
    });
  }, [client]);

  useEffect(() => {
    scrollToBottom();
  }, [waitingForSteps]);

  const mainTextInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (mainTextInputRef.current) {
      mainTextInputRef.current.focus();
      let handler = (event: any) => {
        if (event.data.type === "focusContinueInput") {
          mainTextInputRef.current?.focus();
        }
      };
      window.addEventListener("message", handler);
      return () => {
        window.removeEventListener("message", handler);
      };
    }
  }, [mainTextInputRef]);

  const onMainTextInput = () => {
    if (mainTextInputRef.current) {
      let input = mainTextInputRef.current.value;
      mainTextInputRef.current.value = "";
      if (!client) return;

      if (
        history?.timeline.length &&
        history.timeline[history.current_index].step.name ===
          "Waiting for user input"
      ) {
        if (input.trim() === "") return;
        onStepUserInput(input, history!.current_index);
      } else if (
        history?.timeline.length &&
        history.timeline[history.current_index].step.name ===
          "Waiting for user confirmation"
      ) {
        onStepUserInput("ok", history!.current_index);
      } else {
        if (input.trim() === "") return;

        client.sendMainInput(input);
        setUserInputQueue((queue) => {
          return [...queue, input];
        });
      }
    }

    setWaitingForSteps(true);
  };

  const onStepUserInput = (input: string, index: number) => {
    if (!client) return;
    console.log("Sending step user input", input, index);
    client.sendStepUserInput(input, index);
  };

  // const iterations = useSelector(selectIterations);
  return (
    <>
      <LoadingCover hidden={true} message="Downloading local model..." />
      <TextDialog
        showDialog={showFeedbackDialog}
        onEnter={(text) => {
          client?.sendMainInput(`/feedback ${text}`);
          setShowFeedbackDialog(false);
        }}
        onClose={() => {
          setShowFeedbackDialog(false);
        }}
      ></TextDialog>

      <TopGUIDiv
        ref={topGuiDivRef}
        onKeyDown={(e) => {
          if (e.key === "Enter" && e.ctrlKey) {
            onMainTextInput();
          }
        }}
      >
        {typeof client === "undefined" && (
          <>
            <Loader></Loader>
            <p style={{ textAlign: "center" }}>Loading Continue server...</p>
          </>
        )}
        {history?.timeline.map((node: HistoryNode, index: number) => {
          return (
            <StepContainer
              isLast={index === history.timeline.length - 1}
              isFirst={index === 0}
              open={stepsOpen[index]}
              onToggle={() => {
                const nextStepsOpen = [...stepsOpen];
                nextStepsOpen[index] = !nextStepsOpen[index];
                setStepsOpen(nextStepsOpen);
              }}
              onToggleAll={() => {
                setStepsOpen((prev) => prev.map((_, index) => !prev[index]));
              }}
              key={index}
              onUserInput={(input: string) => {
                onStepUserInput(input, index);
              }}
              inFuture={index > history?.current_index}
              historyNode={node}
              onRefinement={(input: string) => {
                client?.sendRefinementInput(input, index);
              }}
              onReverse={() => {
                client?.reverseToIndex(index);
              }}
              onRetry={() => {
                client?.retryAtIndex(index);
                setWaitingForSteps(true);
              }}
              onDelete={() => {
                client?.deleteAtIndex(index);
              }}
            />
          );
        })}
        {waitingForSteps && <Loader></Loader>}

        <div>
          {userInputQueue.map((input) => {
            return <UserInputQueueItem>{input}</UserInputQueueItem>;
          })}
        </div>

        <ComboBox
          // disabled={
          //   history?.timeline.length
          //     ? history.timeline[history.current_index].step.name ===
          //       "Waiting for user confirmation"
          //     : false
          // }
          ref={mainTextInputRef}
          onEnter={(e) => {
            onMainTextInput();
            e.stopPropagation();
            e.preventDefault();
          }}
          onInputValueChange={() => {}}
          items={availableSlashCommands}
        />
        <ContinueButton onClick={onMainTextInput} />
      </TopGUIDiv>
      <div
        style={{
          position: "fixed",
          bottom: "50px",
          backgroundColor: "white",
          color: "black",
          borderRadius: defaultBorderRadius,
          padding: "16px",
          margin: "16px",
        }}
        hidden={!showDataSharingInfo}
      >
        By turning on this switch, you signal that you would contribute this
        software development data to a publicly accessible, open-source dataset
        in the future.
        <br />
        <br />
        <b>
          {dataSwitchChecked
            ? "No data is being collected. In the future, you would be contributing data"
            : "No data is being collected. In the future, your data would not be shared"}
        </b>
      </div>
      <Footer dataSwitchChecked={dataSwitchChecked}>
        <div
          style={{
            display: "flex",
            gap: "4px",
            marginRight: "auto",
            alignItems: "center",
          }}
          onMouseEnter={() => {
            setShowDataSharingInfo(true);
          }}
          onMouseLeave={() => {
            setShowDataSharingInfo(false);
          }}
        >
          <ReactSwitch
            height={20}
            handleDiameter={20}
            width={40}
            onChange={() => {
              posthog?.capture("data_switch_toggled", {
                vscMachineId: vscMachineId,
                dataSwitchChecked: !dataSwitchChecked,
              });
              setDataSwitchChecked((prev) => !prev);
            }}
            onColor="#12887a"
            checked={dataSwitchChecked}
          />
          <span style={{ cursor: "help", fontSize: "14px" }}>
            Contribute Data
          </span>
        </div>
        <HeaderButtonWithText
          onClick={() => {
            client?.changeDefaultModel(
              usingFastModel ? "gpt-4" : "gpt-3.5-turbo"
            );
            setUsingFastModel((prev) => !prev);
          }}
          text={usingFastModel ? "gpt-3.5-turbo" : "gpt-4"}
        >
          <div
            style={{ fontSize: "18px", marginLeft: "2px", marginRight: "2px" }}
          >
            {usingFastModel ? "⚡" : "🧠"}
          </div>
        </HeaderButtonWithText>
        <HeaderButtonWithText
          onClick={() => {
            client?.sendClear();
          }}
          text="Clear All"
        >
          <Trash size="1.6em" />
        </HeaderButtonWithText>
        <a href="https://continue.dev/docs" className="no-underline">
          <HeaderButtonWithText text="Docs">
            <BookOpen size="1.6em" />
          </HeaderButtonWithText>
        </a>
        <HeaderButtonWithText
          onClick={() => {
            // Set dialog open
            setShowFeedbackDialog(true);
          }}
          text="Feedback"
        >
          <ChatBubbleOvalLeftEllipsis size="1.6em" />
        </HeaderButtonWithText>
      </Footer>
    </>
  );
}

export default GUI;