import { createContext, useContext, useMemo, type ReactNode } from 'react'
import type { ResourceDefinition, ResourceContextValue, Identifier } from '../types'

// =============================================================================
// Single Resource Context (for individual resource pages)
// =============================================================================

export const ResourceContext = createContext<ResourceContextValue | null>(null)

export interface ResourceProviderProps {
  children: ReactNode
  /** Resource name */
  resource: string
  /** Resource definition */
  definition?: ResourceDefinition
  /** Current record ID (if viewing/editing) */
  id?: Identifier
}

/**
 * Provides resource context for a single resource view.
 *
 * @example
 * ```tsx
 * <ResourceProvider resource="users" definition={usersResource} id={userId}>
 *   <UserEdit />
 * </ResourceProvider>
 * ```
 */
export function ResourceProvider({
  children,
  resource,
  definition,
  id,
}: ResourceProviderProps) {
  const value = useMemo<ResourceContextValue>(() => ({
    resource,
    definition,
    id,
    hasList: !!definition?.list,
    hasCreate: !!definition?.create,
    hasEdit: !!definition?.edit,
    hasShow: !!definition?.show,
  }), [resource, definition, id])

  return (
    <ResourceContext.Provider value={value}>
      {children}
    </ResourceContext.Provider>
  )
}

/**
 * Hook to access the current resource context.
 * Must be used within a ResourceProvider.
 *
 * @example
 * ```tsx
 * const { resource, id, hasList, hasEdit } = useResource()
 * ```
 */
export function useResource(): ResourceContextValue {
  const context = useContext(ResourceContext)
  if (!context) {
    throw new Error('useResource must be used within a ResourceProvider')
  }
  return context
}

// =============================================================================
// Resources Collection Context (for app-wide resource registry)
// =============================================================================

export interface ResourcesContextValue {
  /** All registered resources */
  resources: ResourceDefinition[]
  /** Get a resource by name */
  getResource: (name: string) => ResourceDefinition | undefined
  /** Register a new resource */
  registerResource: (resource: ResourceDefinition) => void
  /** Unregister a resource */
  unregisterResource: (name: string) => void
}

export const ResourcesContext = createContext<ResourcesContextValue | null>(null)

export interface ResourcesProviderProps {
  children: ReactNode
  /** Initial resources to register */
  resources?: ResourceDefinition[]
}

/**
 * Provides a registry of all resources in the application.
 *
 * @example
 * ```tsx
 * <ResourcesProvider resources={[usersResource, productsResource]}>
 *   <App />
 * </ResourcesProvider>
 * ```
 */
export function ResourcesProvider({
  children,
  resources: initialResources = [],
}: ResourcesProviderProps) {
  // Use a ref-based state to allow imperative updates without re-renders
  const resourcesRef = useMemo(() => {
    const map = new Map<string, ResourceDefinition>()
    initialResources.forEach((r) => map.set(r.name, r))
    return map
  }, []) // Empty deps - we want this to be stable

  // Initialize from props on mount
  useMemo(() => {
    initialResources.forEach((r) => resourcesRef.set(r.name, r))
  }, [initialResources, resourcesRef])

  const value = useMemo<ResourcesContextValue>(() => ({
    get resources() {
      return Array.from(resourcesRef.values())
    },
    getResource: (name: string) => resourcesRef.get(name),
    registerResource: (resource: ResourceDefinition) => {
      resourcesRef.set(resource.name, resource)
    },
    unregisterResource: (name: string) => {
      resourcesRef.delete(name)
    },
  }), [resourcesRef])

  return (
    <ResourcesContext.Provider value={value}>
      {children}
    </ResourcesContext.Provider>
  )
}

/**
 * Hook to access the resources registry.
 * Must be used within a ResourcesProvider.
 *
 * @example
 * ```tsx
 * const { resources, getResource } = useResources()
 * const usersResource = getResource('users')
 * ```
 */
export function useResources(): ResourcesContextValue {
  const context = useContext(ResourcesContext)
  if (!context) {
    throw new Error('useResources must be used within a ResourcesProvider')
  }
  return context
}
