import type { ModuleAPIs } from "../modules/registry"
import { moduleRegistry } from "../modules/registry"
import type { ClientConfig, RequestOptions } from "../types"
import { HttpClient } from "./base"
import type {
  ErrorInterceptor,
  RequestInterceptor,
  ResponseInterceptor,
} from "./interceptors"
import { commonInterceptors } from "./interceptors"
import { createAPIProxy } from "./proxy"

/**
 * Configuration options for the Follow Client
 */
export interface FollowClientConfig extends Partial<ClientConfig> {
  // Additional SDK-specific configuration
  enableDefaultInterceptors?: boolean
  authToken?: string
}

export class FollowClient {
  private httpClient: HttpClient

  public api: ModuleAPIs = {} as ModuleAPIs

  constructor(config: FollowClientConfig = {}) {
    // Initialize HTTP client with proper defaults
    this.httpClient = new HttpClient({
      baseURL: config.baseURL || "https://api.folo.is",
      timeout: config.timeout || 30000,
      headers: config.headers || {},
      credentials: config.credentials || "include",
      fetch: config.fetch || globalThis.fetch.bind(globalThis),
    })

    // Initialize API route groups
    this.initializeRoutes()

    // Setup default interceptors if enabled
    if (config.enableDefaultInterceptors) {
      this.setupDefaultInterceptors()
    }

    // Set auth token if provided
    if (config.authToken) {
      this.setAuthToken(config.authToken)
    }
  }

  /**
   * Initialize API route groups with proper typing
   */
  private initializeRoutes(): void {
    // Automatically initialize all modules from registry
    for (const [moduleName, moduleDefinition] of Object.entries(
      moduleRegistry,
    )) {
      (this.api as any)[moduleName] = createAPIProxy(this.httpClient, moduleDefinition)
    }
  }

  /**
   * Setup default interceptors
   */
  private setupDefaultInterceptors(): void {
    const interceptors = this.httpClient.getInterceptors()

    // Add default logging interceptor
    interceptors.addRequestInterceptor(
      commonInterceptors.logRequests({ log: console.info }),
    )

    interceptors.addResponseInterceptor(
      commonInterceptors.logResponses({ log: console.info }),
    )
  }

  /**
   * Set authentication token for API requests
   */
  setAuthToken(token: string): void {
    this.httpClient.setHeaders({ Authorization: `Bearer ${token}` })
  }

  /**
   * Remove authentication token
   */
  removeAuthToken(): void {
    const currentHeaders = this.httpClient.getConfig().headers
    const { Authorization, ...newHeaders } = currentHeaders
    this.httpClient.setHeaders(newHeaders)
  }

  /**
   * Set custom headers for API requests
   */
  setHeaders(headers: Record<string, string>): void {
    this.httpClient.setHeaders(headers)
  }

  /**
   * Set custom fetch instance
   */
  setFetch(fetchInstance: typeof fetch): void {
    this.httpClient.setFetch(fetchInstance)
  }

  /**
   * Update client configuration
   */
  updateConfig(config: Partial<FollowClientConfig>): void {
    this.httpClient.setConfig(config)
  }

  /**
   * Get current configuration (readonly)
   */
  getConfig(): Readonly<Required<ClientConfig>> {
    return this.httpClient.getConfig()
  }

  /**
   * Make a custom HTTP request using the underlying HTTP client
   * This allows direct access to the HTTP client for custom requests
   */
  async request<T>(path: string, options?: RequestOptions): Promise<T> {
    return this.httpClient.request<T>(path, options)
  }

  /**
   * Batch multiple requests
   */
  async batch<T extends readonly any[]>(
    requests: readonly [...T],
  ): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
    return Promise.all(requests) as any
  }

  /**
   * Create a new instance with different configuration
   */
  clone(config?: Partial<FollowClientConfig>): FollowClient {
    const currentConfig = this.getConfig()
    return new FollowClient({
      ...currentConfig,
      ...config,
    })
  }

  /**
   * Add request interceptor
   */
  addRequestInterceptor(interceptor: RequestInterceptor): () => void {
    return this.httpClient.getInterceptors().addRequestInterceptor(interceptor)
  }

  /**
   * Add response interceptor
   */
  addResponseInterceptor(interceptor: ResponseInterceptor): () => void {
    return this.httpClient.getInterceptors().addResponseInterceptor(interceptor)
  }

  /**
   * Add error interceptor
   */
  addErrorInterceptor(interceptor: ErrorInterceptor): () => void {
    return this.httpClient.getInterceptors().addErrorInterceptor(interceptor)
  }
}
