/**
 * Auth module — React context + hook for authentication in React Native apps.
 */

import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
import type { PlatformSDK } from '../core.js';

export interface AuthState {
  isAuthenticated: boolean;
  isLoading: boolean;
  userId: string | null;
  email: string | null;
  error: string | null;
}

export interface AuthContextType extends AuthState {
  login: (email: string, password: string) => Promise<void>;
  register: (email: string, password: string, displayName: string) => Promise<void>;
  loginWithGoogle: (idToken: string) => Promise<void>;
  loginWithApple: (idToken: string) => Promise<void>;
  logout: () => Promise<void>;
  refreshSession: () => Promise<void>;
}

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

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

interface AuthProviderProps {
  sdk: PlatformSDK;
  children: React.ReactNode;
  /** Called when tokens are received — persist to secure storage */
  onTokens?: (access: string, refresh: string) => void;
  /** Called on logout — clear secure storage */
  onLogout?: () => void;
}

export function AuthProvider({
  sdk,
  children,
  onTokens,
  onLogout,
}: AuthProviderProps): React.JSX.Element {
  const [state, setState] = useState<AuthState>({
    isAuthenticated: false,
    isLoading: true,
    userId: null,
    email: null,
    error: null,
  });

  const handleTokenResponse = useCallback(
    async (res: Response) => {
      if (!res.ok) {
        const body = (await res.json().catch(() => ({ message: 'Login failed' }))) as {
          message?: string;
        };
        throw new Error(body.message ?? `HTTP ${res.status}`);
      }
      const data = (await res.json()) as {
        accessToken?: string;
        refreshToken?: string;
        user?: { id?: string; email?: string };
      };
      if (data.accessToken && data.refreshToken) {
        onTokens?.(data.accessToken, data.refreshToken);
      }
      setState({
        isAuthenticated: true,
        isLoading: false,
        userId: data.user?.id ?? null,
        email: data.user?.email ?? null,
        error: null,
      });
    },
    [onTokens]
  );

  const login = useCallback(
    async (email: string, password: string) => {
      setState(s => ({ ...s, isLoading: true, error: null }));
      try {
        const res = await sdk.fetch('/auth/login', {
          method: 'POST',
          body: JSON.stringify({ email, password, productId: sdk.config.productId }),
        });
        await handleTokenResponse(res);
      } catch (e: unknown) {
        const msg = e instanceof Error ? e.message : 'Login failed';
        setState(s => ({ ...s, isLoading: false, error: msg }));
      }
    },
    [sdk, handleTokenResponse]
  );

  const register = useCallback(
    async (email: string, password: string, displayName: string) => {
      setState(s => ({ ...s, isLoading: true, error: null }));
      try {
        const res = await sdk.fetch('/auth/register', {
          method: 'POST',
          body: JSON.stringify({
            email,
            password,
            displayName,
            productId: sdk.config.productId,
          }),
        });
        await handleTokenResponse(res);
      } catch (e: unknown) {
        const msg = e instanceof Error ? e.message : 'Registration failed';
        setState(s => ({ ...s, isLoading: false, error: msg }));
      }
    },
    [sdk, handleTokenResponse]
  );

  const loginWithGoogle = useCallback(
    async (idToken: string) => {
      setState(s => ({ ...s, isLoading: true, error: null }));
      try {
        const res = await sdk.fetch('/auth/oauth/google', {
          method: 'POST',
          body: JSON.stringify({ idToken }),
        });
        await handleTokenResponse(res);
      } catch (e: unknown) {
        const msg = e instanceof Error ? e.message : 'Google login failed';
        setState(s => ({ ...s, isLoading: false, error: msg }));
      }
    },
    [sdk, handleTokenResponse]
  );

  const loginWithApple = useCallback(
    async (idToken: string) => {
      setState(s => ({ ...s, isLoading: true, error: null }));
      try {
        const res = await sdk.fetch('/auth/oauth/apple', {
          method: 'POST',
          body: JSON.stringify({ idToken }),
        });
        await handleTokenResponse(res);
      } catch (e: unknown) {
        const msg = e instanceof Error ? e.message : 'Apple login failed';
        setState(s => ({ ...s, isLoading: false, error: msg }));
      }
    },
    [sdk, handleTokenResponse]
  );

  const logout = useCallback(async () => {
    try {
      await sdk.fetch('/auth/logout', { method: 'POST' });
    } catch {
      /* best-effort */
    }
    onLogout?.();
    setState({
      isAuthenticated: false,
      isLoading: false,
      userId: null,
      email: null,
      error: null,
    });
  }, [sdk, onLogout]);

  const refreshSession = useCallback(async () => {
    setState(s => ({ ...s, isLoading: true }));
    try {
      const res = await sdk.fetch('/auth/me');
      if (res.ok) {
        const data = (await res.json()) as { id?: string; email?: string };
        setState({
          isAuthenticated: true,
          isLoading: false,
          userId: data.id ?? null,
          email: data.email ?? null,
          error: null,
        });
      } else {
        setState(s => ({ ...s, isAuthenticated: false, isLoading: false }));
      }
    } catch {
      setState(s => ({ ...s, isLoading: false }));
    }
  }, [sdk]);

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

  const value: AuthContextType = {
    ...state,
    login,
    register,
    loginWithGoogle,
    loginWithApple,
    logout,
    refreshSession,
  };

  return React.createElement(AuthContext.Provider, { value }, children);
}
