"use client";

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useState,
  ReactNode,
} from "react";

type AuthUser = {
  uuid: string;
  email: string;
  username?: string | null;
  passwordSetAt?: string | null;
  displayName?: string | null;
  role: string;
} | null;

export type RegisterParams = {
  email: string;
  password: string;
  displayName?: string;
  acceptedTos: boolean;
  tosVersion: string;
};

type AuthContextValue = {
  user: AuthUser;
  loading: boolean;
  login: (params: { email: string; password: string }) => Promise<void>;
  register: (params: RegisterParams) => Promise<void>;
  logout: () => void;
  refreshUser: () => Promise<void>;
  getAuthHeaders: () => HeadersInit;
};

const AuthContext = createContext<AuthContextValue | undefined>(undefined);

const USER_KEY = "ballistic_auth_user";

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<AuthUser>(null);
  const [loading, setLoading] = useState<boolean>(true);

  /**
   * Persist user to localStorage (for quick initial render)
   */
  const persistUser = useCallback((nextUser: AuthUser) => {
    if (typeof window === "undefined") return;

    if (nextUser) {
      window.localStorage.setItem(USER_KEY, JSON.stringify(nextUser));
    } else {
      window.localStorage.removeItem(USER_KEY);
    }
  }, []);

  /**
   * Hydrate from localStorage and refresh from server
   */
  useEffect(() => {
    if (typeof window === "undefined") return;

    const storedUser = window.localStorage.getItem(USER_KEY);

    // Quick hydration from cached user
    if (storedUser) {
      try {
        const parsedUser = JSON.parse(storedUser);
        setUser(parsedUser);
      } catch {
        window.localStorage.removeItem(USER_KEY);
      }
    }

    // Verify session with server
    fetch("/api/auth/me", { credentials: 'include' })
      .then((res) => {
        if (res.ok) {
          return res.json();
        }
        throw new Error("Not authenticated");
      })
      .then((data) => {
        const nextUser: AuthUser = {
          uuid: data.uuid,
          email: data.email,
          username: data.username || null,
          displayName: data.displayName || null,
          role: data.role || "USER",
          passwordSetAt: data.passwordSetAt || null,
        };
        setUser(nextUser);
        persistUser(nextUser);
      })
      .catch(() => {
        setUser(null);
        persistUser(null);
      })
      .finally(() => {
        setLoading(false);
      });
  }, [persistUser]);

  const refreshUser = useCallback(async () => {
    const res = await fetch("/api/auth/me", {
      cache: "no-store",
      credentials: 'include',
    });

    if (!res.ok) {
      const text = await res.text().catch(() => res.statusText);
      throw new Error(text || "Failed to refresh user");
    }

    const data = await res.json();

    const nextUser: AuthUser = {
      uuid: data.uuid,
      email: data.email,
      username: data.username || null,
      displayName: data.displayName || null,
      role: data.role || "USER",
      passwordSetAt: data.passwordSetAt || null,
    };

    setUser(nextUser);
    persistUser(nextUser);
  }, [persistUser]);

  const login = useCallback(
    async ({ email, password }: { email: string; password: string }) => {
      setLoading(true);
      try {
        const res = await fetch("/api/auth/login", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          credentials: 'include',
          body: JSON.stringify({ email, password }),
        });

        if (!res.ok) {
          const errorData = await res.json().catch(() => ({}));
          throw new Error(errorData.error || "Login failed");
        }

        const data = await res.json();

        const nextUser: AuthUser = data.user;

        setUser(nextUser);
        persistUser(nextUser);
      } finally {
        setLoading(false);
      }
    },
    [persistUser]
  );

  const register = useCallback(
    async ({
      email,
      password,
      displayName,
      acceptedTos,
      tosVersion,
    }: RegisterParams) => {
      setLoading(true);
      try {
        const res = await fetch("/api/auth/register", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          credentials: 'include',
          body: JSON.stringify({
            email,
            password,
            displayName,
            acceptedTos,
            tosVersion,
          }),
        });

        if (!res.ok) {
          const errorData = await res.json().catch(() => ({}));
          throw new Error(errorData.error || "Registration failed");
        }

        const data = await res.json();

        const nextUser: AuthUser = data.user;

        setUser(nextUser);
        persistUser(nextUser);
      } finally {
        setLoading(false);
      }
    },
    [persistUser]
  );

  const logout = useCallback(() => {
    setUser(null);
    persistUser(null);

    // Clear server session cookies
    fetch("/api/auth/logout", {
      method: "POST",
      credentials: 'include',
    }).catch(() => {});
  }, [persistUser]);

  const getAuthHeaders = useCallback((): HeadersInit => {
    if (typeof window === "undefined") return {};
    const token =
      localStorage.getItem("token") ||
      localStorage.getItem("jwt") ||
      localStorage.getItem("accessToken");
    return token ? { Authorization: `Bearer ${token}` } : {};
  }, []);

  const value: AuthContextValue = {
    user,
    loading,
    login,
    register,
    logout,
    refreshUser,
    getAuthHeaders,
  };

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return ctx;
}
