import { useRouter } from "expo-router";
import React, { createContext, useContext, useEffect, useState } from "react";
import {
  deleteSetting,
  getSetting,
  initDB,
  salvarUsuario,
  setSetting,
} from "@/services/db";
import { setAuthToken } from "@/server/api";
import { getUserIdFromToken } from "@/utils/jwt";

type User = {
  id: string;
  email: string;
  name: string;
};

type AuthContextType = {
  user: User | null;
  token: string | null;
  isLoading: boolean;
  login: (
    email: string,
    password: string,
    token: string,
    user: User,
  ) => Promise<void>;
  signup: (
    name: string,
    email: string,
    userId: string,
    token: string,
  ) => Promise<void>;
  logout: () => Promise<void>;
  updateUser: (patch: Partial<Pick<User, "name">>) => Promise<void>;
  isSignedIn: boolean;
};

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

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [token, setToken] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const router = useRouter();

  useEffect(() => {
    bootstrapAsync();
  }, []);

  const bootstrapAsync = async () => {
    try {
      await initDB();
      const storedToken = await getSetting("authToken");
      const storedUser = await getSetting("user");

      if (storedToken && storedUser) {
        let parsedUser: User = JSON.parse(storedUser);
        const realId = getUserIdFromToken(storedToken);

        if (realId && realId !== parsedUser.id) {
          await salvarUsuario({
            id: realId,
            email: parsedUser.email,
            name: parsedUser.name,
            token: storedToken,
          });
          parsedUser = { ...parsedUser, id: realId };
          await setSetting("user", JSON.stringify(parsedUser));
        }

        setAuthToken(storedToken);
        setToken(storedToken);
        setUser(parsedUser);
        router.replace("/home");
      } else {
        setToken(null);
        setUser(null);
        router.replace("/");
      }
    } catch (e) {
      console.error("Failed to restore token", e);
    } finally {
      setIsLoading(false);
    }
  };

  const authContext: AuthContextType = {
    user,
    token,
    isLoading,
    isSignedIn: token !== null,
    login: async (
      _email: string,
      _password: string,
      authToken: string,
      authUser: User,
    ) => {
      try {
        setAuthToken(authToken);
        setToken(authToken);
        setUser(authUser);
        await setSetting("authToken", authToken);
        await setSetting("user", JSON.stringify(authUser));
        router.replace("/home");
      } catch (error) {
        console.error("Login failed:", error);
        throw error;
      }
    },
    signup: async (
      name: string,
      email: string,
      userId: string,
      authToken: string,
    ) => {
      try {
        const newUser: User = {
          id: userId,
          email,
          name,
        };

        setToken(authToken);
        setUser(newUser);
        await setSetting("authToken", authToken);
        await setSetting("user", JSON.stringify(newUser));
        router.replace("/home");
      } catch (error) {
        console.error("Signup failed:", error);
        throw error;
      }
    },
    logout: async () => {
      try {
        await deleteSetting("authToken");
        await deleteSetting("user");
        setAuthToken(null);
        setToken(null);
        setUser(null);
        router.replace("/");
      } catch (error) {
        console.error("Logout failed:", error);
        throw error;
      }
    },
    updateUser: async (patch) => {
      if (!user) return;
      const updated: User = { ...user, ...patch };
      setUser(updated);
      await setSetting("user", JSON.stringify(updated));
    },
  };

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

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