import React, { useState, useEffect, KeyboardEvent } from "react";
import { Input, Paper, Typography } from "@mui/material";

import { Session } from "./GlobalContext";
import { UserList } from "./UserList";
import "./App.css";
import { ws_base, base } from "./Common";
import { Box, Button } from "@mui/material";
import { BrowserRouter as Router, Route, Routes, useParams } from "react-router-dom";
import useWebSocket, { ReadyState } from "react-use-websocket";

console.log(`AI Voice Chat Build: ${process.env.REACT_APP_AI_VOICECHAT_BUILD}`);

type LobbyProps = {
  session: Session;
  setSession: React.Dispatch<React.SetStateAction<Session | null>>;
  setError: React.Dispatch<React.SetStateAction<string | null>>;
};

type Lobby = {
  id: string;
  name: string;
  private: boolean;
};

const LobbyView: React.FC<LobbyProps> = (props: LobbyProps) => {
  const { session, setSession, setError } = props;
  const { lobbyName = "default" } = useParams<{ lobbyName: string }>();
  const [lobby, setLobby] = useState<Lobby | null>(null);
  const [editName, setEditName] = useState<string>("");
  const [socketUrl, setSocketUrl] = useState<string | null>(null);
  const [creatingLobby, setCreatingLobby] = useState<boolean>(false);
  
  const socket = useWebSocket(socketUrl, {
    onOpen: () => console.log("app - WebSocket connection opened."),
    onClose: () => console.log("app - WebSocket connection closed."),
    onError: (event) => console.error("app - WebSocket error observed:", event),
    // onMessage: (event) => console.log("WebSocket message received:"),
    // shouldReconnect: (closeEvent) => true, // Will attempt to reconnect on all close events.
    reconnectInterval: 3000,
    share: true,
  });
  const { sendJsonMessage, lastJsonMessage, readyState } = socket;

  useEffect(() => {
    if (lobby && session) {
      setSocketUrl(`${ws_base}/${lobby.id}/${session.id}`);
    }
  }, [lobby, session]);

  useEffect(() => {
    if (!lastJsonMessage || !session) {
      return;
    }
    const data: any = lastJsonMessage;
    switch (data.type) {
      case "update":
        if ("name" in data) {
          console.log(`Lobby - name set to ${data.name}`);
          setSession((s) => (s ? { ...s, name: data.name } : null));
        }
        break;
      case "error":
        console.error(`Lobby - Server error: ${data.error}`);
        setError(data.error);
        break;
      default:
        break;
    }
  }, [lastJsonMessage, session, setError, setSession]);

  useEffect(() => {
    console.log("app - WebSocket connection status: ", readyState);
  }, [readyState]);

  useEffect(() => {
    if (!session || !lobbyName || creatingLobby || (lobby && lobby.name === lobbyName)) {
      return;
    }
    const getLobby = async (lobbyName: string, session: Session) => {
      const res = await fetch(`${base}/api/lobby/${session.id}`, {
        method: "POST",
        cache: "no-cache",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          type: "lobby_create",
          data: {
            name: lobbyName,
            private: false,
          },
        }),
      });

      if (res.status >= 400) {
        const error = `Unable to connect to AI Voice Chat server! Try refreshing your browser in a few seconds.`;
        console.error(error);
        setError(error);
      }

      const data = await res.json();
      if (data.error) {
        console.error(`Lobby - Server error: ${data.error}`);
        setError(data.error);
        return;
      }
      if (data.type !== "lobby_created") {
        console.error(`Lobby - Unexpected response type: ${data.type}`);
        setError(`Unexpected response from server`);
        return;
      }
      const lobby: Lobby = data.data;
      console.log(`Lobby - Joined lobby`, lobby);
      setLobby(lobby);
    };

    setCreatingLobby(true);
    getLobby(lobbyName, session).then(() => {
      setCreatingLobby(false);
    });
  }, [session, lobbyName, setLobby, setError]);

  const setName = (name: string) => {
    sendJsonMessage({
      type: "set_name",
      data: { name },
    });
  };

  const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {
    if (event.key === "Enter") {
      event.preventDefault();
      const newName = editName.trim();
      if (!newName || session?.name === newName) {
        return;
      }
      setName(newName);
      setEditName("");
    }
  };

  return (
    <Paper className="Lobby" sx={{ p: 2, m: 2, width: "fit-content" }}>
      {readyState !== ReadyState.OPEN || !session ? (
        <h2>Connecting to server...</h2>
      ) : (
        <>
          <Box sx={{ mb: 2, display: "flex", gap: 2, alignItems: "flex-start", flexDirection: "column" }}>
            <Box>
              <Typography variant="h5">AI Voice Chat Lobby: {lobbyName}</Typography>
            </Box>
            <Box>
              {session.name && <Typography>You are logged in as: {session.name}</Typography>}
              {!session.name && (
                <Box sx={{ gap: 1, display: "flex", flexDirection: "column", alignItems: "flex-start" }}>
                  <Typography>Enter your name to join:</Typography>
                  <Box sx={{ display: "flex", gap: 1, width: "100%" }}>
                    <Input
                      type="text"
                      value={editName}
                      onChange={(e): void => {
                        setEditName(e.target.value);
                      }}
                      onKeyDown={handleKeyDown}
                      placeholder="Your name"
                    />
                    <Button
                      variant="contained"
                      onClick={() => {
                        setName(editName);
                        setEditName("");
                      }}
                      disabled={!editName.trim()}
                    >
                      Join
                    </Button>
                  </Box>
                </Box>
              )}
            </Box>
          </Box>

          {session.name && (
            <>
              {/* {session.lobbies.map((lobby: string) => (
                <Box key={lobby}>
                  <Button variant="contained" disabled={lobby === lobbyName} sx={{ mr: 1, mb: 1 }}>
                    {lobby === lobbyName ? `In Lobby: ${lobby}` : `Join Lobby: ${lobby}`}
                  </Button>
                </Box>
              ))} */}

              {session && socketUrl && <UserList socketUrl={socketUrl} session={session} />}
            </>
          )}
        </>
      )}
    </Paper>
  );
};

const App = () => {
  const [session, setSession] = useState<Session | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (error) {
      setTimeout(() => setError(null), 5000);
    }
  }, [error]);

  useEffect(() => {
    if (!session) {
      return;
    }
    console.log(`App - sessionId`, session.id);
  }, [session]);

  useEffect(() => {
    if (session) {
      return;
    }
    const getSession = async () => {
      const res = await fetch(`${base}/api/session`, {
        method: "GET",
        cache: "no-cache",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
        },
      });

      if (res.status >= 400) {
        const error = `Unable to connect to AI Voice Chat server! Try refreshing your browser in a few seconds.`;
        console.error(error);
        setError(error);
        return;
      }
      const data = await res.json();
      if (data.error) {
        console.error(`App - Server error: ${data.error}`);
        setError(data.error);
        return;
      }
      setSession(data);
    };

    getSession();
  }, [session, setSession]);

  return (
    <Box>
      {!session && <h2>Connecting to server...</h2>}
      {session && (
        <Router>
          <Routes>
            <Route element={<LobbyView {...{ setError, session, setSession }} />} path={`${base}/:lobbyName`} />
            <Route element={<LobbyView {...{ setError, session, setSession }} />} path={`${base}`} />
          </Routes>
        </Router>
      )}
      {error && (
        <Paper className="Error" sx={{ p: 2, m: 2, width: "fit-content", backgroundColor: "#ffdddd" }}>
          <Typography color="red">{error}</Typography>
        </Paper>
      )}
    </Box>
  );
};

export default App;
