import createContextHook from "@nkzw/create-context-hook";
import { useState, useCallback, useMemo, useEffect } from "react";
import { trpcClient } from "@/lib/trpc";
import { toUserSafeErrorMessage } from "@/lib/utils/alert";
import { isApiConfigured } from "@/lib/app-config";
import {
  getAuthToken,
  setAuthToken,
  clearAuthToken,
  getDemoModeFlag,
  setDemoModeFlag,
} from "@/lib/mobile/secure-auth";
import type { PlatformPlanFeatures, PlatformPlanStatus, ZendoPlanTier } from "@/backend/db/schema";

export interface User {
  id: string;
  email?: string;
  username: string;
  displayName: string;
  phoneNumber?: string;
  avatar?: string;
  role: string;
  verificationStatus: string;
  accountTier: string;
  balance: number;
  privacyModeEnabled?: boolean;
  profileVisibility?: "public" | "friends" | "private" | "contacts" | "hidden";
  payPerMessagePrice?: number;
  platformPlan?: {
    tier: ZendoPlanTier | null;
    status: PlatformPlanStatus;
    renewalAt: Date | string | null;
    cancelAtPeriodEnd?: boolean;
    subscriptionId?: string | null;
    plan: {
      name: string;
      monthlyPrice: number;
      tagline?: string;
      featureList?: string[];
      features: PlatformPlanFeatures;
    } | null;
  };
}

interface AuthState {
  user: User | null;
  token: string | null;
  isLoading: boolean;
  isAuthenticated: boolean;
  isDemoMode: boolean;
}

interface AuthResult {
  success: boolean;
  error?: string;
  requires2FA?: boolean;
  challengeId?: string;
}

function getErrorMessage(error: unknown, fallback: string): string {
  return toUserSafeErrorMessage(error, fallback);
}

function isNetworkError(error: unknown): boolean {
  const message = error instanceof Error ? error.message : String(error);
  const lower = message.toLowerCase();
  return (
    lower.includes("network request failed") ||
    lower.includes("failed to fetch") ||
    lower.includes("fetch failed") ||
    lower.includes("html instead of json") ||
    lower.includes("could not connect") ||
    lower.includes("load failed")
  );
}

export const [AuthProvider, useAuth] = createContextHook(() => {
  const [authState, setAuthState] = useState<AuthState>({
    user: null,
    token: null,
    isLoading: true,
    isAuthenticated: false,
    isDemoMode: false,
  });

  const loadStoredAuth = useCallback(async () => {
    try {
      const token = await getAuthToken();
      if (token) {
        const user = await trpcClient.auth.me.query();
        if (user) {
          setAuthState({
            user,
            token,
            isLoading: false,
            isAuthenticated: true,
            isDemoMode: false,
          });
          return;
        }
        await clearAuthToken();
      }
    } catch (error) {
      if (__DEV__) {
        console.error("Failed to load stored auth:", error);
      }
    }
    setAuthState({
      user: null,
      token: null,
      isLoading: false,
      isAuthenticated: false,
      isDemoMode: false,
    });
  }, []);

  const initializeAuth = useCallback(async () => {
    try {
      if (await getDemoModeFlag()) {
        await setDemoModeFlag(false);
      }
      await loadStoredAuth();
    } catch {
      setAuthState({
        user: null,
        token: null,
        isLoading: false,
        isAuthenticated: false,
        isDemoMode: false,
      });
    }
  }, [loadStoredAuth]);

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

  const login = useCallback(async (identifier: string, password: string): Promise<AuthResult> => {
    if (!isApiConfigured()) {
      return {
        success: false,
        error: "Zendo is not connected to a server. Set EXPO_PUBLIC_API_BASE_URL and rebuild.",
      };
    }

    try {
      const response = await trpcClient.auth.login.mutate({ identifier, password });
      if (response.requires2FA) {
        return {
          success: true,
          requires2FA: true,
          challengeId: response.challengeId,
        };
      }
      await setAuthToken(response.token);
      await setDemoModeFlag(false);
      setAuthState({
        user: response.user,
        token: response.token,
        isLoading: false,
        isAuthenticated: true,
        isDemoMode: false,
      });
      return { success: true };
    } catch (error) {
      if (isNetworkError(error)) {
        return { success: false, error: "Couldn't reach Zendo. Check your connection and try again." };
      }
      return { success: false, error: getErrorMessage(error, "Invalid email/phone or password") };
    }
  }, []);

  const verifyTwoFactorLogin = useCallback(async (challengeId: string, code: string): Promise<AuthResult> => {
    try {
      const response = await trpcClient.auth.verifyTwoFactorLogin.mutate({ challengeId, code });
      await setAuthToken(response.token);
      await setDemoModeFlag(false);
      setAuthState({
        user: response.user,
        token: response.token,
        isLoading: false,
        isAuthenticated: true,
        isDemoMode: false,
      });
      return { success: true };
    } catch (error) {
      return { success: false, error: getErrorMessage(error, "Invalid authentication code") };
    }
  }, []);

  const register = useCallback(async (data: {
    email?: string;
    phoneNumber?: string;
    password: string;
    displayName: string;
  }): Promise<AuthResult & { username?: string }> => {
    if (!isApiConfigured()) {
      return {
        success: false,
        error: "Zendo is not connected to a server. Set EXPO_PUBLIC_API_BASE_URL and rebuild.",
      };
    }

    try {
      const response = await trpcClient.auth.register.mutate(data);
      await setAuthToken(response.token);
      await setDemoModeFlag(false);
      setAuthState({
        user: response.user,
        token: response.token,
        isLoading: false,
        isAuthenticated: true,
        isDemoMode: false,
      });
      return { success: true, username: response.user.username };
    } catch (error) {
      if (isNetworkError(error)) {
        return { success: false, error: "Couldn't reach Zendo. Check your connection and try again." };
      }
      return { success: false, error: getErrorMessage(error, "Registration failed. Please try again.") };
    }
  }, []);

  const logout = useCallback(async () => {
    try {
      await clearAuthToken();
    } finally {
      await setDemoModeFlag(false);
      setAuthState({
        user: null,
        token: null,
        isLoading: false,
        isAuthenticated: false,
        isDemoMode: false,
      });
    }
  }, []);

  const refreshUser = useCallback(async () => {
    if (!authState.token) {
      return;
    }
    try {
      const user = await trpcClient.auth.me.query();
      if (user) {
        setAuthState((prev) => ({ ...prev, user }));
      }
    } catch (error) {
      if (__DEV__) {
        console.error("Failed to refresh user:", error);
      }
    }
  }, [authState.token]);

  const updateDemoBalance = useCallback((_newBalance: number) => {}, []);

  const updateDemoUser = useCallback((updates: Partial<User>) => {
    setAuthState((prev) => ({
      ...prev,
      user: prev.user ? { ...prev.user, ...updates } : null,
    }));
  }, []);

  const forgotPassword = useCallback(async (identifier: string): Promise<AuthResult & { devResetToken?: string }> => {
    try {
      const response = await trpcClient.auth.forgotPassword.mutate({ identifier });
      return { success: true, devResetToken: (response as { devResetToken?: string }).devResetToken };
    } catch (error) {
      if (isNetworkError(error)) {
        return { success: false, error: "Couldn't reach the server. Check your connection and try again." };
      }
      return { success: false, error: getErrorMessage(error, "Something went wrong. Please try again.") };
    }
  }, []);

  const resetPassword = useCallback(async (
    identifier: string,
    token: string,
    newPassword: string
  ): Promise<AuthResult> => {
    try {
      await trpcClient.auth.resetPassword.mutate({ identifier, token, newPassword });
      return { success: true };
    } catch (error) {
      return { success: false, error: getErrorMessage(error, "Invalid or expired reset code") };
    }
  }, []);

  const deleteAccount = useCallback(async (password: string): Promise<AuthResult> => {
    try {
      await trpcClient.settings.deleteAccount.mutate({ password, confirmation: "DELETE" });
      await clearAuthToken();
      await setDemoModeFlag(false);
      setAuthState({
        user: null,
        token: null,
        isLoading: false,
        isAuthenticated: false,
        isDemoMode: false,
      });
      return { success: true };
    } catch (error) {
      return { success: false, error: getErrorMessage(error, "Failed to delete account") };
    }
  }, []);

  const resetDemo = useCallback(async () => {}, []);

  const loginAsDemo = useCallback(async (): Promise<AuthResult> => {
    return { success: false, error: "Demo accounts are not available. Sign up or sign in with your account." };
  }, []);

  return useMemo(() => ({
    ...authState,
    login,
    verifyTwoFactorLogin,
    register,
    logout,
    refreshUser,
    updateDemoBalance,
    updateDemoUser,
    resetDemo,
    loginAsDemo,
    forgotPassword,
    resetPassword,
    deleteAccount,
  }), [
    authState,
    login,
    verifyTwoFactorLogin,
    register,
    logout,
    refreshUser,
    updateDemoBalance,
    updateDemoUser,
    resetDemo,
    loginAsDemo,
    forgotPassword,
    resetPassword,
    deleteAccount,
  ]);
});
