/* eslint-disable @typescript-eslint/no-empty-object-type */
import type {
  HTTPMethod,
  RequestContentType,
  ResponseContentType,
} from "../types"

/**
 * Route definition with type annotations
 */
export interface RouteDefinition<TInput = any, TResponse = any> {
  method: HTTPMethod
  path: string
  params?: readonly string[]
  /** Fields that should be sent as query parameters */
  query?: readonly string[]
  /** Fields that should be sent as request body */
  body?: readonly string[]
  input?: TInput
  response?: TResponse
  requestType?: RequestContentType
  responseType?: ResponseContentType
}

/**
 * Nested routes structure - supports any depth of nesting
 */
export type NestedRoutes = {
  [key: string]: RouteDefinition | NestedRoutes
}

/**
 * Module definition interface
 */
export interface ModuleDefinition<TRoutes extends NestedRoutes> {
  name: string
  prefix?: string
  routes: TRoutes
  api: ModuleAPI<TRoutes>
}

/**
 * Fetch options for route requests
 */
interface FetchOptions {
  headers?: Record<string, string>
  timeout?: number
  signal?: AbortSignal
}

/**
 * Check if arguments are required
 */
type IsRequired<TInput> = [TInput] extends [never] ?
  false :
    {} extends TInput ?
      false :
      true

/**
 * Route function with proper argument requirements
 */
export type RouteFunction<TInput, TResponse> =
  IsRequired<TInput> extends true ?
      (args: TInput, options?: FetchOptions) => Promise<TResponse> :
      (args?: TInput, options?: FetchOptions) => Promise<TResponse>

/**
 * Generate API types from route structure
 */
export type ModuleAPI<TRoutes> = {
  [K in keyof TRoutes]: TRoutes[K] extends RouteDefinition<
    infer TInput,
    infer TResponse
  > ?
    RouteFunction<TInput, TResponse> :
    TRoutes[K] extends NestedRoutes ?
      ModuleAPI<TRoutes[K]> :
      never;
}

/**
 * Legacy route args interface for backward compatibility
 */
export interface LegacyRouteArgs {
  params?: Record<string, string>
  query?: Record<string, string | number | boolean>
  body?: unknown
  headers?: Record<string, string>
  timeout?: number
  signal?: AbortSignal
}

/**
 * Helper function to define a single route with proper type inference
 */
export function defineRoute<TInput = never, TResponse = never>(
  method: HTTPMethod,
  path: string,
  options: {
    /** Fields that should be sent as query parameters */
    query?: readonly string[]
    /** Fields that should be sent as request body */
    body?: readonly string[]
    input?: TInput
    response?: TResponse
    requestType?: RequestContentType
    responseType?: ResponseContentType
  } = {},
): RouteDefinition<TInput, TResponse> {
  // Extract parameters from path string
  const params = extractParamsFromPath(path)

  return {
    method,
    path,
    params: params.length > 0 ? params : undefined,
    query: options.query,
    body: options.body,
    input: options.input,
    response: options.response,
    requestType: options.requestType,
    responseType: options.responseType,
  }
}

/**
 * Extract parameter names from a path string
 * e.g. "/users/{userId}/posts" -> ["userId"]
 */
function extractParamsFromPath(path: string): string[] {
  const paramRegex = /\{([^}]+)\}/g
  const params: string[] = []
  let match

  while ((match = paramRegex.exec(path)) !== null) {
    params.push(match[1])
  }

  return params
}

/**
 * Helper type to infer params from path string
 */
export type InferParams<T extends string> =
  T extends `${string}{${infer Param}}${infer Rest}` ?
    { [K in Param]: string } & InferParams<Rest> :
      {}

/**
 * Define a module with routes and metadata
 */
export function defineModule<TRoutes extends NestedRoutes>(definition: {
  name: string
  prefix?: string
  routes: TRoutes
}): ModuleDefinition<TRoutes> {
  return {
    ...definition,
    api: {} as ModuleAPI<TRoutes>, // Will be created by proxy
  }
}
