{% include '_header.jinja' %}

/**
 * {{ api_title }} - API Client with JWT Management
 *
 * Usage:
 * ```typescript
 * import { API } from './api';
 *
 * const api = new API('https://api.example.com');
 *
 * // Set JWT token
 * api.setToken('your-jwt-token', 'refresh-token');
 *
 * // Use API
 * const posts = await api.posts.list();
 * const user = await api.users.retrieve(1);
 *
 * // Check authentication
 * if (api.isAuthenticated()) {
 *   // ...
 * }
 *
 * // Custom storage with logging (for Electron/Node.js)
 * import { MemoryStorageAdapter, APILogger } from './storage';
 * const logger = new APILogger({ enabled: true, logLevel: 'debug' });
 * const api = new API('https://api.example.com', {
 *   storage: new MemoryStorageAdapter(logger),
 *   loggerConfig: { enabled: true, logLevel: 'debug' }
 * });
 *
 * // Get OpenAPI schema
 * const schema = api.getSchema();
 * ```
 */

import { APIClient } from "./client";
import {
  StorageAdapter,
  LocalStorageAdapter,
  CookieStorageAdapter,
  MemoryStorageAdapter
} from "./storage";
import type { RetryConfig } from "./retry";
import type { LoggerConfig } from "./logger";
import { APILogger } from "./logger";
{% for tag in tags %}
import { {{ tag.class_name }} } from "./{{ tag.slug }}/client";
{% endfor %}
{% for tag in tags %}
export * as {{ tag.class_name }}Types from "./{{ tag.slug }}/models";
// Note: Direct exports (export * from) are removed to avoid duplicate type conflicts
// Use namespace exports like CfgAccountsTypes.User or import from specific modules
{% endfor %}
{% if has_enums %}
export * as Enums from "./enums";
{% endif %}
{% if generate_zod_schemas %}

// Re-export Zod schemas for runtime validation
export * as Schemas from "./_utils/schemas";
// Also export all schemas directly for convenience
export * from "./_utils/schemas";

// Re-export Zod validation events for browser integration
export type { ValidationErrorDetail, ValidationErrorEvent } from "./validation-events";
export { dispatchValidationError, onValidationError, formatZodError } from "./validation-events";
{% endif %}
{% if generate_fetchers %}

// Re-export typed fetchers for universal usage
export * as Fetchers from "./_utils/fetchers";
export * from "./_utils/fetchers";

// Re-export API instance configuration functions
export {
  configureAPI,
  getAPIInstance,
  reconfigureAPI,
  clearAPITokens,
  resetAPI,
  isAPIConfigured
} from "./api-instance";
{% endif %}
{% if generate_swr_hooks %}
// NOTE: SWR hooks are generated in ./_utils/hooks/ but NOT exported here to keep
// the main bundle server-safe. Import hooks directly from the hooks directory:
//   import { useUsers } from './_utils/hooks';
// Or use a separate entry point like '@djangocfg/api/hooks' for client components.
{% endif %}

// Re-export core client
export { APIClient };

// Re-export storage adapters for convenience
export type { StorageAdapter };
export { LocalStorageAdapter, CookieStorageAdapter, MemoryStorageAdapter };

// Re-export error classes for convenience
export { APIError, NetworkError } from "./errors";

// Re-export HTTP adapters for custom implementations
export type { HttpClientAdapter, HttpRequest, HttpResponse } from "./http";
export { FetchAdapter } from "./http";

// Re-export logger types and classes
export type { LoggerConfig, RequestLog, ResponseLog, ErrorLog } from "./logger";
export { APILogger } from "./logger";

// Re-export retry configuration and utilities
export type { RetryConfig, FailedAttemptInfo } from "./retry";
export { withRetry, shouldRetry, DEFAULT_RETRY_CONFIG } from "./retry";

export const TOKEN_KEY = "auth_token";
export const REFRESH_TOKEN_KEY = "refresh_token";

/** Auto-detect locale from cookie NEXT_LOCALE or navigator.language */
function detectLocale(): string | null {
  try {
    if (typeof document !== 'undefined') {
      const match = document.cookie.match(/(?:^|;\s*)NEXT_LOCALE=([^;]*)/);
      if (match) return match[1];
    }
    if (typeof navigator !== 'undefined' && navigator.language) {
      return navigator.language;
    }
  } catch {}
  return null;
}

export interface APIOptions {
  /** Custom storage adapter (defaults to LocalStorageAdapter) */
  storage?: StorageAdapter;
  /** Retry configuration for failed requests */
  retryConfig?: RetryConfig;
  /** Logger configuration */
  loggerConfig?: Partial<LoggerConfig>;
  /** Locale for Accept-Language header (e.g. 'en', 'ko', 'ru') */
  locale?: string;
}

export class API {
  private baseUrl: string;
  private _client: APIClient;
  private _token: string | null = null;
  private _refreshToken: string | null = null;
  private _locale: string | null = null;
  private storage: StorageAdapter;
  private options?: APIOptions;

  // Sub-clients
{% for tag in tags %}
  public {{ tag.property }}!: {{ tag.class_name }};
{% endfor %}

  constructor(baseUrl: string, options?: APIOptions) {
    this.baseUrl = baseUrl;
    this.options = options;

    // Create logger if config provided
    const logger = options?.loggerConfig ? new APILogger(options.loggerConfig) : undefined;

    // Initialize storage with logger
    this.storage = options?.storage || new LocalStorageAdapter(logger);

    this._locale = options?.locale || null;

    this._loadTokensFromStorage();

    // Initialize APIClient with token getter for URL authentication
    this._client = new APIClient(this.baseUrl, {
      retryConfig: this.options?.retryConfig,
      loggerConfig: this.options?.loggerConfig,
      tokenGetter: () => this.getToken(),
    });

    // Always inject auth header wrapper (reads token dynamically from storage)
    this._injectAuthHeader();

    // Initialize sub-clients from APIClient
{% for tag in tags %}
    this.{{ tag.property }} = this._client.{{ tag.property }};
{% endfor %}
  }

  private _loadTokensFromStorage(): void {
    this._token = this.storage.getItem(TOKEN_KEY);
    this._refreshToken = this.storage.getItem(REFRESH_TOKEN_KEY);
  }

  private _reinitClients(): void {
    this._client = new APIClient(this.baseUrl, {
      retryConfig: this.options?.retryConfig,
      loggerConfig: this.options?.loggerConfig,
      tokenGetter: () => this.getToken(),
    });

    // Always inject auth header wrapper (reads token dynamically from storage)
    this._injectAuthHeader();

    // Reinitialize sub-clients
{% for tag in tags %}
    this.{{ tag.property }} = this._client.{{ tag.property }};
{% endfor %}
  }

  private _injectAuthHeader(): void {
    // Override request method to inject auth header
    const originalRequest = this._client.request.bind(this._client);
    this._client.request = async <T>(
      method: string,
      path: string,
      options?: { params?: Record<string, any>; body?: any; formData?: FormData; headers?: Record<string, string> }
    ): Promise<T> => {
      // Read token from storage dynamically (supports JWT injection after instantiation)
      const token = this.getToken();
      const locale = this._locale || detectLocale();
      const mergedOptions = {
        ...options,
        headers: {
          ...(options?.headers || {}),
          ...(token ? { 'Authorization': `Bearer ${token}` } : {}),
          ...(locale ? { 'Accept-Language': locale } : {}),
        },
      };

      return originalRequest(method, path, mergedOptions);
    };
  }

  /**
   * Get current JWT token
   */
  getToken(): string | null {
    return this.storage.getItem(TOKEN_KEY);
  }

  /**
   * Get current refresh token
   */
  getRefreshToken(): string | null {
    return this.storage.getItem(REFRESH_TOKEN_KEY);
  }

  /**
   * Set JWT token and refresh token
   * @param token - JWT access token
   * @param refreshToken - JWT refresh token (optional)
   */
  setToken(token: string, refreshToken?: string): void {
    this._token = token;
    this.storage.setItem(TOKEN_KEY, token);

    if (refreshToken) {
      this._refreshToken = refreshToken;
      this.storage.setItem(REFRESH_TOKEN_KEY, refreshToken);
    }

    // Reinitialize clients with new token
    this._reinitClients();
  }

  /**
   * Clear all tokens
   */
  clearTokens(): void {
    this._token = null;
    this._refreshToken = null;
    this.storage.removeItem(TOKEN_KEY);
    this.storage.removeItem(REFRESH_TOKEN_KEY);

    // Reinitialize clients without token
    this._reinitClients();
  }

  /**
   * Check if user is authenticated
   */
  isAuthenticated(): boolean {
    return !!this.getToken();
  }

  /**
   * Update base URL and reinitialize clients
   * @param url - New base URL
   */
  setBaseUrl(url: string): void {
    this.baseUrl = url;
    this._reinitClients();
  }

  /**
   * Get current base URL
   */
  getBaseUrl(): string {
    return this.baseUrl;
  }

  /**
   * Set locale for Accept-Language header
   * @param locale - Locale string (e.g. 'en', 'ko', 'ru') or null to clear
   */
  setLocale(locale: string | null): void {
    this._locale = locale;
  }

  /**
   * Get current locale
   */
  getLocale(): string | null {
    return this._locale;
  }

  /**
   * Get OpenAPI schema path
   * @returns Path to the OpenAPI schema JSON file
   *
   * Note: The OpenAPI schema is available in the schema.json file.
   * You can load it dynamically using:
   * ```typescript
   * const schema = await fetch('./schema.json').then(r => r.json());
   * // or using fs in Node.js:
   * // const schema = JSON.parse(fs.readFileSync('./schema.json', 'utf-8'));
   * ```
   */
  getSchemaPath(): string {
    return './schema.json';
  }
}

export default API;
