import React, { useEffect, useState } from "react";

import { useSessionStorage } from "../hooks/useStorage";

import { useApi } from "./ApiContext";

export interface User {
  id: string;
  email: string;
  is_superuser: boolean;
  groups: string[];
  full_name: string;
  username: string;
  permissions: string[];
}

interface UserContext {
  user: User | null | undefined;
  login: (username: string, password: string) => Promise<User | null>;
  logout: () => Promise<void>;
  changePassword: (
    oldPassword: string,
    newPassword1: string,
    newPassword2: string,
  ) => Promise<void>;
  /**
   * Horrid name! This is to prevent a flash of loginscreen or ui when getting an already logged in user.
   */
  hasTriedToFetchUser: boolean;
}

const UserContext = React.createContext<UserContext>(undefined as unknown as UserContext);
export const useUser = () => React.useContext(UserContext);

export const UserContextProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
  const api = useApi();
  const [user, setUser] = useSessionStorage<User | undefined>("bcom-admin.user");
  const [hasTriedToFetchUser, setHasTriedToFetchUser] = useState(false);

  useEffect(() => {
    const getInitialUser = async () => {
      if (api == null) return;
      const user = (await api.getAuthenticatedUser()) ?? undefined; // `undefined` has meaning here! It resets the sessionStorage.
      setUser(user);
      setHasTriedToFetchUser(true);
    };

    if (!hasTriedToFetchUser) getInitialUser();
  }, [hasTriedToFetchUser, user, api]);

  const login = async (username: string, password: string) => {
    const response = await api?.operations["bananas.login:create"].call({
      body: { username, password },
    });

    if (response?.ok) {
      const user = await response.json();
      setUser(user);
      return user;
    } else {
      setUser(undefined);
      return null;
    }
  };

  const changePassword = async (
    oldPassword: string,
    newPassword1: string,
    newPassword2: string,
  ) => {
    const response = await api?.operations["bananas.change_password:create"].call({
      body: {
        old_password: oldPassword,
        new_password1: newPassword1,
        new_password2: newPassword2,
      },
    });

    if (!response?.ok) {
      throw new Error("Failed to change password");
    }
  };

  const logout = async () => {
    const response = await api?.operations["bananas.logout:create"].call();

    if (response?.ok) {
      setUser(undefined);
      return;
    }

    throw new Error("Could not log out");
  };

  return (
    <UserContext.Provider value={{ user, login, logout, changePassword, hasTriedToFetchUser }}>
      {children}
    </UserContext.Provider>
  );
};

export default UserContext;
