import {Meta} from '@storybook/addon-docs/blocks'

<Meta title='@dxtmisha/en/functional-basic/# Api'/>

# Api

The `Api` class is a central utility for making HTTP requests. It provides a simple interface for common methods and manages global settings like base URL, default headers, and lifecycle hooks.

### Key Capabilities

The class provides a comprehensive set of tools for managing network communication:
- **CRUD Methods** — execution of `GET`, `POST`, `PUT`, `PATCH`, `DELETE` requests.
- **Global Configuration** — setting up base URLs, origins, headers, and parameters.
- **Lifecycle Hooks** — handling events before and after the request (e.g., for Refresh Token).
- **Hydration (SSR)** — transferring data from server to client to prevent duplicate requests.
- **Caching** — saving responses in memory or external storage (LocalStorage).
- **Response Emulation** — creating mock data for development and testing.
- **Automatic Localization** — replacement of `{locale}`, `{country}`, and `{language}` markers in request paths.
- **Loading and Error Management** — integration with global indicator components and error centers.

## Basic Usage

The simplest way to make a request is by using the static methods `get`, `post`, `put`, or `delete`. All methods support response typing via Generics.

```javascript
import { Api } from '@dxtmisha/functional-basic'

// Simple GET request with typing
const data = await Api.get<User[]>({ path: 'users' })

// POST request with data in the body
const newUser = await Api.post<User>({
  path: 'users',
  request: { name: 'John Doe', job: 'Developer' }
})
```

## Global Configuration

You can configure the API behavior globally, for example, during application initialization. This removes the need to specify full paths and common headers in every call.

```javascript
// Set the base URL for all relative paths
Api.setUrl('/api/')

// Set the origin (protocol and domain) for the base URL
// If urlRoot starts with '/', the origin will be prepended to it
Api.setOrigin('https://api.example.com')

// Add common headers (e.g., an authorization token)
Api.setHeaders({
  'Authorization': 'Bearer YOUR_TOKEN'
})

// Set common parameters to be added to every request
Api.setRequestDefault({
  app_version: '1.0.0'
})

// Check if the server is running on localhost
if (Api.isLocalhost()) {
  console.log('Running locally')
}
```

### setConfig Method

Allows you to set all main settings with a single object.

```javascript
Api.setConfig({
  urlRoot: '/api/',
  origin: 'https://api.example.com',
  headers: { 'Authorization': 'Bearer ...' },
  timeout: 5000,
  devMode: true
})
```

## URL Handling

When forming the final URL, the class automatically replaces special markers with current values from the localization settings (`Geo`):
- `{locale}` — current locale (e.g., `en-US`).
- `{country}` — current country (e.g., `us`).
- `{language}` — current language (e.g., `en`).

```javascript
// If the current locale is 'en-US', the final path will be '/api/en-US/users'
const data = await Api.get({ path: '{locale}/users' })
```

## Status and Meta-information

The `Api` class automatically extracts metadata from the server response (e.g., `success`, `status`, `message`, `code`) and merges it with your data object. This allows you to receive both the payload and the execution result in a single object.

You can access the full status of the last request via the `getStatus()` method, which returns an instance of `ApiStatus`.

```javascript
const data = await Api.get({ path: 'users' })
const status = Api.getStatus()

console.log(status.getStatus())     // 200 (HTTP Status)
console.log(status.getStatusType()) // 'success' | 'error' | 'warning' | 'info'
console.log(status.getMessage())    // Server-provided message or status text
console.log(status.getResponse())   // Raw parsed response body
```

## Error Identification

The library provides a sophisticated error management system via `ApiError`. You can pre-register known API error patterns, and the system will automatically identify them based on status, codes, or custom validation functions.

```javascript
import { ApiError, ApiMethodItem } from '@dxtmisha/functional-basic'

// Registering a specific error pattern
ApiError.add({
  url: 'users/login',
  method: ApiMethodItem.post,
  code: 'INVALID_CREDENTIALS',
  message: 'Please check your username and password.'
})

// Processing the request
const status = Api.getStatus()
const errorItem = status.get()?.errorObject // ApiErrorItem instance

if (errorItem && errorItem.getCode() === 'INVALID_CREDENTIALS') {
  // Handle specific error case
}
```

## Lifecycle Hooks

The library allows you to inject global logic that will trigger for every request. This is useful for dynamically adding tokens, logging, or centralized server error handling.

### Preparation Hook (setPreparation)

Executed immediately before a request is sent to the server.

- **Parameters**: Receives the `apiFetch` object, which contains all parameters of the current request (path, method, data, etc.).
- **Usage**: Checking access rights, adding dynamic parameters to `apiFetch.request`, or simply logging the start of an operation.

```javascript
Api.setPreparation(async (apiFetch) => {
  // You can modify apiFetch before sending
  console.log(`Starting request to: ${apiFetch.path}`)
})
```

### End Hook (setEnd)

Executed after the server returns a response, but before the data reaches your code.

- **Parameters**: Receives the native `Response` object and the `apiFetch` settings object.
- **Returns**: A Promise with an object `{ reset?: boolean, data?: any }`.
- **Reset Flag**: If `true` is returned, the library will abort the current chain and execute the request again. This is useful for automatically handling an expired token (Refresh Token).
- **Data Field**: Allows you to replace or supplement the data returned by the `Api.request` method.

```javascript
Api.setEnd(async (response, apiFetch) => {
  // For example, handling a 401 Unauthorized error
  if (response.status === 401) {
    const isRefreshed = await myAuthService.refreshToken()
    
    if (isRefreshed) {
      return { reset: true } // Retry the request with the new token
    }
  }
  
  return {} // Continue with standard processing
})
```

## Hydration

Hydration allows transferring data obtained during server-side rendering (SSR) to the client side. This prevents the same requests from being executed again immediately after the page loads.

1. **On the Server**: Perform the necessary requests and get the script via `Api.getHydrationScript()`.
2. **In the Template**: Insert the resulting string into the HTML.
3. **On the Client**: Upon `Api` initialization, the data will be automatically loaded into the response manager.

```javascript
// SSR Example
await Api.get({ path: 'settings' });
const hydrationScript = Api.getHydrationScript(); // <script>...</script>
```

## Caching

The `Api` class supports automatic response caching. You can use either the built-in in-memory storage or connect external storage (e.g., `LocalStorage`).

```javascript
import { ApiCache, DataStorage } from '@dxtmisha/functional-basic';

// Connecting external storage (optional)
ApiCache.init(
  async (key) => DataStorage.get(key),
  async (key, value) => DataStorage.set(key, value)
);

// Usage in a request
const data = await Api.get({
  path: 'catalog/list',
  cache: 3600 // Cache for 1 hour (in seconds)
});
```

## Response Emulation

The built-in `ApiResponse` manager (accessible via `Api.getResponse()`) allows you to intercept requests and return mock data. This is extremely useful for UI development when the backend is not ready, or for writing tests.

### Registering Mocks

You can add both static responses and dynamic functions.

```javascript
const responseManager = Api.getResponse()

// 1. Static response for a GET request
responseManager.add({
  path: 'profile',
  method: 'GET',
  response: { id: 1, name: 'Admin' },
  lag: true // Simulate network delay (0-2 sec)
})

// 2. Dynamic response based on request parameters
responseManager.add({
  path: 'users/search',
  method: 'GET',
  response: (request) => {
    return {
      results: [`Result for: ${request.query}`],
      count: 1
    }
  }
})

// 3. Intercept a POST request with any body
responseManager.add({
  path: 'users/update',
  method: 'POST',
  request: '*any', // Ignore request body validation
  response: { success: true }
})
```

### Developer Mode

For easier debugging, you can enable emulation logging in the console. In this mode, the manager will warn in the console about every intercepted request.

```javascript
Api.getResponse().setDevMode(true)
```

The emulator triggers **before** the real `fetch` is executed. If no matching mock is found, `Api` will perform a regular network request.

## Advanced: ApiInstance

The `Api` class is a **static proxy** for a global instance of `ApiInstance`. This instance is stored in `ServerStorage`, ensuring isolation between requests during SSR and singleton behavior on the client.

For advanced scenarios—such as connecting to multiple independent APIs simultaneously or creating temporary isolated environments—you can instantiate `ApiInstance` directly.

```javascript
import { ApiInstance } from '@dxtmisha/functional-basic'

const secondaryApi = new ApiInstance()
secondaryApi.setUrl('https://another-api.com/')
secondaryApi.setHeaders({ 'X-Client-Id': '...' })

const data = await secondaryApi.get({ path: 'resource' })
```
