import { Accessor, createSignal, useContext } from "solid-js";
import { createContext } from "solid-js";

import { Route, Routes, A } from "@solidjs/router";

import { createPost, getPosts } from "./api";
import { Post, NewPost } from "./api";

// Representing the state of varoious modals.
// So far we only have one modal, but we can add more later
// by adding more fields to this interface, or maybe an enum
interface ModalContextType {
  loginModalOpen: Accessor<boolean>;
  setLoginModalOpen: (value: boolean) => void;
}

interface LoginContextType {
  token: Accessor<string>;
  setToken: (value: string) => void;
  username: Accessor<string>;
  setUsername: (value: string) => void;
}

// It is unclear to me if this is the idiomatic way to do this in Solid
export const ModalContext = createContext<ModalContextType>();
export const LoginContext = createContext<LoginContextType>();

function Root() {
  // All of these are passed into context providers
  const [loginModalOpen, setLoginModalOpen] = createSignal(false);
  const [token, setToken] = createSignal("");
  const [username, setUsername] = createSignal("");

  // This may not be the best place to do this.
  localStorage.getItem("token") && setToken(localStorage.getItem("token")!);
  localStorage.getItem("username") &&
    setUsername(localStorage.getItem("username")!);

  return (
    <>
      <ModalContext.Provider value={{ loginModalOpen, setLoginModalOpen }}>
        <LoginContext.Provider
          value={{ token, setToken, username, setUsername }}
        >
          <div class="flex flex-col items-center my-2">
            <Navbar />
            <Login />
            <div class="flex flex-col items-center md:w-96 space-y-2">
              <Primary />
            </div>
          </div>
        </LoginContext.Provider>
      </ModalContext.Provider>
    </>
  );
}

function Navbar() {
  let modal_ctx = useContext(ModalContext);
  let login_ctx = useContext(LoginContext);
  return (
    <div class="navbar bg-base-100 max-w-3xl max-w flex justify-evenly">
      <a class="btn btn-ghost normal-case text-xl">FrostByte</a>
      <Menu />
      <A
        href="#"
        class="btn btn-ghost normal-case text-sm"
        onClick={(b) => {
          b.preventDefault();
          modal_ctx?.setLoginModalOpen(true);
        }}
      >
        {login_ctx?.token() != "" ? login_ctx?.username() : "Login"}
      </A>
    </div>
  );
}

function Menu() {
  return (
    <ul class="menu menu-horizontal bg-base-200 rounded-box space-x-2 justify-end">
      <li>
        <A href="/" end>
          Home
        </A>
      </li>
      <li>
        <A href="/new" end>
          New
        </A>
      </li>
    </ul>
  );
}

function NewPostInputArea() {
  const [content, setContent] = createSignal("");
  const login_ctx = useContext(LoginContext);

  return (
    <div class="flex flex-col space-y-2">
      <textarea
        class="textarea textarea-bordered"
        placeholder="Speak your mind..."
        maxLength={500}
        oninput={(input) => {
          setContent(input.target.value);
        }}
      ></textarea>
      <button
        class={
          "btn btn-primary self-end btn-sm" +
          (content() == "" ? " btn-disabled" : "")
        }
        onclick={() => {
          if (content() == "") return;
          createPost({
            content: content(),
            token: login_ctx?.token(),
          } as NewPost);
        }}
      >
        Submit
      </button>
    </div>
  );
}

function Posts() {
  const [posts, setPosts] = createSignal([] as Post[]);
  const [loading, setLoading] = createSignal(true);

  getPosts().then((posts) => {
    setPosts(posts as any);
    setLoading(false);
  });

  return (
    <div class="flex flex-col space-y-2 w-full md:w-96">
      {loading() ? (
        <span class="loading loading-spinner loading-lg self-center"></span>
      ) : (
        <></>
      )}
      {posts().map((post) => {
        if (post.content == "") return; // Filtering out empty posts, remove this later
        return <PostSegment post={post}></PostSegment>;
      })}
    </div>
  );
}

function PostSegment({ post }: { post: Post }) {
  return (
    <div class="card bg-base-200 shadow-lg compact text-base-content w-full">
      <div class="card-body">
        <p class="text-base-content break-words">{post.content}</p>
      </div>
    </div>
  );
}

function Primary() {
  return (
    <Routes>
      <Route path="/" element={<Posts />} />
      <Route path="/new" element={<NewPostInputArea />} />
    </Routes>
  );
}

// This is a modal
function Login() {
  const modal_ctx = useContext(ModalContext);
  return (
    <dialog id="login_modal" class="modal" open={modal_ctx?.loginModalOpen()}>
      <div class="modal-box">
        <h3 class="font-bold text-lg">Hello!</h3>
        <p class="py-4">Login to your FrostByte account.</p>
        <LoginForm />
      </div>
      <form
        method="dialog"
        // This backdrop renders choppy on my machine. Likely because of the blur filter or misuse of css transisions
        class="modal-backdrop backdrop-brightness-50 backdrop-blur-sm backdrop-contrast-100 transition-all transition-300"
        onsubmit={(e) => {
          // This is just needed to set the state to false
          // The modal will close itself without this code, but without setting the state
          e.preventDefault();
          modal_ctx?.setLoginModalOpen(false);
        }}
      >
        <button>close</button>
      </form>
    </dialog>
  );
}

// This function is responsible for sending the login request to the server
// and storing the token in localstorage
async function submitLogin(
  username: string,
  password: string
): Promise<string> {
  const response = await fetch("/api/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ username, password }),
  });

  if (response.ok) {
    const data = await response.json();
    if (data.token && data.username) {
      localStorage.setItem("token", data.token);
      localStorage.setItem("username", data.username);
      return data.token;
    }
  }
  return "";
}

function LoginForm() {
  const modal_ctx = useContext(ModalContext);
  const [username, setUsername] = createSignal("");
  const [password, setPassword] = createSignal("");
  const [waiting, setWaiting] = createSignal(false);
  const [error, setError] = createSignal(false);

  async function loginFailed() {
    setError(true);
    setWaiting(false);
    setTimeout(() => {
      setError(false);
    }, 1000);
  }

  return (
    <form class="form-control">
      <label class="label">
        <span class="label-text">Username</span>
      </label>
      <input
        type="text"
        placeholder="username"
        class="input input-bordered"
        onChange={(e) => {
          setUsername(e.target.value);
        }}
      />
      <label class="label">
        <span class="label-text">Password</span>
      </label>
      <input
        type="password"
        placeholder="password"
        class="input input-bordered"
        onChange={(e) => {
          setPassword(e.target.value);
        }}
      />
      <button
        class={"btn btn-primary mt-4" + (error() ? " btn-error" : "")}
        onClick={(b) => {
          b.preventDefault();
          setWaiting(true);
          submitLogin(username(), password()).then((token) => {
            if (token != "") {
              setWaiting(false);
              setError(false);
              modal_ctx?.setLoginModalOpen(false);
            } else {
              loginFailed();
            }
          });
        }}
      >
        {waiting() ? "Logging in..." : "Login"}
      </button>
    </form>
  );
}

export default Root;
