import React, { createContext, useContext, useEffect, useState } from "react";
import { SetSnackType } from '../components/Snack';
import { User, Guest, Candidate } from 'types/types';
import { ApiClient } from "services/api-client";
import { debugConversion } from "types/conversion";

type UserContextType = {
  apiClient: ApiClient;
  user: User | null;
  guest: Guest;
  candidate: Candidate | null;
  setUser: (user: User | null) => void;
  setCandidate: (candidate: Candidate | null) => void;
};

const UserContext = createContext<UserContextType | undefined>(undefined);

const useUser = () => {
  const ctx = useContext(UserContext);
  if (!ctx) throw new Error("useUser must be used within a UserProvider");
  return ctx;
};

interface UserProviderProps {
  children: React.ReactNode;
  setSnack: SetSnackType;
};

const UserProvider: React.FC<UserProviderProps> = (props: UserProviderProps) => {
  const { children, setSnack } = props;
  const [apiClient, setApiClient] = useState<ApiClient>(new ApiClient());
  const [candidate, setCandidate] = useState<Candidate | null>(null);
  const [guest, setGuest] = useState<Guest | null>(null);
  const [user, setUser] = useState<User | null>(null);
  const [activeUser, setActiveUser] = useState<User | null>(null);

  useEffect(() => {
    console.log("Candidate =>", candidate);
  }, [candidate]);

  useEffect(() => {
    console.log("Guest =>", guest);
  }, [guest]);

  /* If the user changes to a non-null value, create a new
   * apiClient with the access token */
  useEffect(() => {
    console.log("User => ", user);
    if (user === null) {
      return;
    }
    /* This apiClient will persist until the user is changed
     * or logged out */
    const accessToken = localStorage.getItem('accessToken');
    if (!accessToken) {
      throw Error("accessToken is not set for user!");
    }
    setApiClient(new ApiClient(accessToken));
  }, [user]);

  /* Handle logout if any consumers of UserProvider setUser to NULL */
  useEffect(() => {
    /* If there is an active user and it is the same as the
     * new user, do nothing */
    if (activeUser && activeUser.email === user?.email) {
      return;
    }

    const logout = async () => {
      if (!activeUser) {
        return;
      }
      console.log(`Logging out ${activeUser.email}`);
      try {
        const accessToken = localStorage.getItem('accessToken');
        const refreshToken = localStorage.getItem('refreshToken');
        if (!accessToken || !refreshToken) {
          setSnack("Authentication tokens are invalid.", "error");
          return;
        }
        const results = await apiClient.logout(accessToken, refreshToken);
        if (results.error) {
          console.error(results.error);
          setSnack(results.error.message, "error")
        }
      } catch (e) {
        console.error(e);
        setSnack(`Unable to logout: ${e}`, "error")
      }

      localStorage.removeItem('accessToken');
      localStorage.removeItem('refreshToken');
      localStorage.removeItem('userData');
      createGuestSession();
      setUser(null);
    };

    setActiveUser(user);
    if (!user) {
      logout();
    }
  }, [user, apiClient, activeUser]);

  const createGuestSession = () => {
    console.log("TODO: Convert this to query the server for the session instead of generating it.");
    const sessionId = `guest_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const guest: Guest = {
      sessionId,
      createdAt: new Date(),
      lastActivity: new Date(),
      ipAddress: 'unknown',
      userAgent: navigator.userAgent
    };
    setGuest(guest);
    debugConversion(guest, 'Guest Session');
  };

  const checkExistingAuth = () => {
    const accessToken = localStorage.getItem('accessToken');
    const userData = localStorage.getItem('userData');
    if (accessToken && userData) {
      try {
        const user = JSON.parse(userData);
        // Convert dates back to Date objects if they're stored as strings
        if (user.createdAt && typeof user.createdAt === 'string') {
          user.createdAt = new Date(user.createdAt);
        }
        if (user.updatedAt && typeof user.updatedAt === 'string') {
          user.updatedAt = new Date(user.updatedAt);
        }
        if (user.lastLogin && typeof user.lastLogin === 'string') {
          user.lastLogin = new Date(user.lastLogin);
        }
        setApiClient(new ApiClient(accessToken));
        setUser(user);
      } catch (e) {
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        localStorage.removeItem('userData');
      }
    }
  };

  // Create guest session on component mount
  useEffect(() => {
    createGuestSession();
    checkExistingAuth();
  }, []);

  if (guest === null) {
    return <></>;
  }

  return (
    <UserContext.Provider value={{ apiClient, candidate, setCandidate, user, setUser, guest }}>
      {children}
    </UserContext.Provider>
  );
};

export {
    UserProvider,
    useUser
};