'use client';

import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
import { createApiClient } from '@bytelyst/api-client';
import type { AuthConfig, AuthContextValue, BaseUser } from './types.js';

/**
 * Create a typed auth provider + hook for a specific user type.
 *
 * @example
 * ```tsx
 * const { AuthProvider, useAuth } = createAuthProvider<AdminUser>({
 *   storagePrefix: "admin",
 *   loginEndpoint: "/auth/login",
 *   mapLoginResponse: (data) => ({
 *     user: data.user,
 *     accessToken: data.accessToken,
 *     refreshToken: data.refreshToken,
 *   }),
 * });
 * ```
 */
export function createAuthProvider<TUser extends BaseUser = BaseUser>(config: AuthConfig<TUser>) {
  const { storagePrefix, loginEndpoint, mapLoginResponse, onLoginFallback, onLogout } = config;

  const USER_KEY = `${storagePrefix}_auth_user`;
  const TOKEN_KEY = `${storagePrefix}_access_token`;
  const REFRESH_KEY = `${storagePrefix}_refresh_token`;

  const AuthContext = createContext<AuthContextValue<TUser> | null>(null);

  function getStoredUser(): TUser | null {
    if (typeof window === 'undefined') return null;
    try {
      const stored = localStorage.getItem(USER_KEY);
      return stored ? JSON.parse(stored) : null;
    } catch {
      return null;
    }
  }

  function AuthProvider({ children }: { children: ReactNode }) {
    const [user, setUser] = useState<TUser | null>(getStoredUser);
    const isLoading = false;

    const api = createApiClient({
      baseUrl: '/api',
      getToken: () => (typeof window !== 'undefined' ? localStorage.getItem(TOKEN_KEY) : null),
    });

    const login = useCallback(
      async (email: string, password: string) => {
        const { data, error } = await api.safeFetch<unknown>(loginEndpoint, {
          method: 'POST',
          body: JSON.stringify({ email, password }),
        });

        if (data && !error) {
          const mapped = mapLoginResponse(data);
          setUser(mapped.user);
          localStorage.setItem(USER_KEY, JSON.stringify(mapped.user));
          localStorage.setItem(TOKEN_KEY, mapped.accessToken);
          localStorage.setItem(REFRESH_KEY, mapped.refreshToken);
          return true;
        }

        // Try fallback (e.g. mock credentials) when API is unavailable
        if (error && onLoginFallback) {
          const fallback = await onLoginFallback(email, password, error);
          if (fallback) {
            setUser(fallback.user);
            localStorage.setItem(USER_KEY, JSON.stringify(fallback.user));
            localStorage.setItem(TOKEN_KEY, fallback.accessToken);
            localStorage.setItem(REFRESH_KEY, fallback.refreshToken);
            return true;
          }
        }

        return false;
      },
      [api]
    );

    const logout = useCallback(() => {
      setUser(null);
      localStorage.removeItem(USER_KEY);
      localStorage.removeItem(TOKEN_KEY);
      localStorage.removeItem(REFRESH_KEY);
      onLogout?.();
    }, []);

    return (
      <AuthContext.Provider value={{ user, isAuthenticated: !!user, isLoading, login, logout }}>
        {children}
      </AuthContext.Provider>
    );
  }

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

  return { AuthProvider, useAuth };
}
