import type { ModuleDefinition } from "../shared/define-module"
import { RouteResolver } from "../shared/route-resolver"
import type {
  HTTPMethod,
  RequestContentType,
  RequestOptions,
  ResponseContentType,
} from "../types"
import type { HttpClient } from "./base"

/**
 * Route function arguments for legacy compatibility
 */
export interface LegacyRouteArgs {
  params?: Record<string, string>
  query?: Record<string, string | number | boolean>
  body?: unknown
  headers?: Record<string, string>
  timeout?: number
  signal?: AbortSignal
}

/**
 * Route function arguments with flattened input
 */
export interface RouteArgs extends Record<string, any> {
  headers?: Record<string, string>
  timeout?: number
  signal?: AbortSignal
}

/**
 * Route function type
 */
export type RouteFunction<T = any> = (args?: RouteArgs) => Promise<T>

/**
 * Route definition for API endpoints (from module system)
 */
export interface RouteDefinition {
  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[]
  requestType?: RequestContentType
  responseType?: ResponseContentType
}

/**
 * Dynamic proxy handler for API routes
 */
export class APIProxyHandler {
  private routes: Map<string, RouteDefinition>
  private client: HttpClient

  constructor(client: HttpClient, routes: Map<string, RouteDefinition>) {
    this.client = client
    this.routes = routes
  }

  /**
   * Proxy get trap that creates route functions dynamically
   */
  get(target: any, propKey: string | symbol): any {
    const routeKey = String(propKey)

    // Handle special properties
    if (routeKey === "constructor" || routeKey === "prototype") {
      return target[propKey]
    }

    // Handle nested routes (e.g., feeds.claim.challenge)
    if (routeKey.includes(".")) {
      return this.handleNestedRoute(routeKey)
    }

    const route = this.routes.get(routeKey)

    if (!route) {
      // For nested routes, return a proxy that continues the chain
      if (this.hasNestedRoute(routeKey)) {
        return this.createNestedProxy(routeKey)
      }

      throw new Error(`Route '${routeKey}' not found`)
    }

    return this.createRouteFunction(route)
  }

  /**
   * Check if a route key has nested routes
   */
  private hasNestedRoute(prefix: string): boolean {
    for (const routeKey of this.routes.keys()) {
      if (routeKey.startsWith(`${prefix}.`)) {
        return true
      }
    }
    return false
  }

  /**
   * Create a nested proxy for route chains
   */
  private createNestedProxy(prefix: string): any {
    const nestedRoutes = new Map<string, RouteDefinition>()

    // Find all routes that start with the prefix
    for (const [routeKey, route] of this.routes.entries()) {
      if (routeKey.startsWith(`${prefix}.`)) {
        const nestedKey = routeKey.slice(Math.max(0, prefix.length + 1))
        nestedRoutes.set(nestedKey, route)
      }
    }

    return new Proxy({}, new APIProxyHandler(this.client, nestedRoutes))
  }

  /**
   * Handle nested route calls
   */
  private handleNestedRoute(routeKey: string): RouteFunction {
    const route = this.routes.get(routeKey)

    if (!route) {
      throw new Error(`Nested route '${routeKey}' not found`)
    }

    return this.createRouteFunction(route)
  }

  /**
   * Create a route function from a route definition
   */
  private createRouteFunction(route: RouteDefinition): RouteFunction {
    return async (args: RouteArgs = {}) => {
      const { headers, timeout, signal, ...inputArgs } = args

      // Check if this is using the old API structure (legacy compatibility)

      const params: Record<string, string> = {}
      const query: Record<string, string | number | boolean> = {}
      let body: unknown

      // Handle new flattened API structure
      const flattenedArgs = { ...inputArgs }

      // Extract parameters from flattenedArgs based on route definition
      if (route.params) {
        route.params.forEach((param) => {
          if (flattenedArgs[param] !== undefined) {
            params[param] = String(flattenedArgs[param])
            // Remove param from flattenedArgs to avoid duplication
            delete flattenedArgs[param]
          }
        })
      }

      // Extract query parameters based on route definition
      if (route.query) {
        route.query.forEach((field) => {
          if (flattenedArgs[field] !== undefined) {
            query[field] = flattenedArgs[field]
            // Remove from flattenedArgs to avoid duplication
            delete flattenedArgs[field]
          }
        })
      }

      // Extract body parameters based on route definition
      if (route.body) {
        const bodyData: Record<string, any> = {}
        route.body.forEach((field) => {
          if (flattenedArgs[field] !== undefined) {
            bodyData[field] = flattenedArgs[field]
            // Remove from flattenedArgs to avoid duplication
            delete flattenedArgs[field]
          }
        })
        if (Object.keys(bodyData).length > 0) {
          body = bodyData
        }
      }

      // Handle remaining fields with fallback logic (backward compatibility)
      // For GET requests, remaining args go to query
      // For POST/PUT/PATCH/DELETE requests, remaining args go to body
      // Filter out undefined values
      const remainingFields = Object.keys(flattenedArgs).filter(
        (key) => flattenedArgs[key] !== undefined,
      )
      if (remainingFields.length > 0) {
        const remainingData = Object.fromEntries(
          remainingFields.map((key) => [key, flattenedArgs[key]]),
        )

        if (route.method === "GET") {
          Object.assign(query, remainingData)
        } else {
          // For non-GET requests, merge remaining args into body
          if (body && typeof body === "object") {
            Object.assign(body, remainingData)
          } else {
            body = remainingData
          }
        }
      }

      // Build URL with parameters
      let url = route.path
      if (route.params) {
        route.params.forEach((param) => {
          const value = params[param]
          if (value !== undefined) {
            url = url.replace(`{${param}}`, encodeURIComponent(value))
          }
        })
      }

      // Build request options
      const requestOptions: RequestOptions = {
        method: route.method,
        headers,
        timeout,
        signal,
        requestType: route.requestType,
      }

      // Add query parameters if they exist
      if (Object.keys(query).length > 0) {
        requestOptions.query = query
      }

      // Add body if it exists
      if (body !== undefined) {
        requestOptions.body = body
      }

      return this.client.request(url, requestOptions)
    }
  }
}

/**
 * Create a typed API proxy from a module definition
 */
export function createAPIProxy<T extends Record<string, any>>(
  client: HttpClient,
  module: ModuleDefinition<any>,
): T {
  // Flatten nested routes and apply prefix
  const flatRoutes = RouteResolver.flattenRoutes(module.routes, module.prefix)

  const routeMap = new Map(Object.entries(flatRoutes))
  return new Proxy({}, new APIProxyHandler(client, routeMap)) as T
}

// Legacy functions removed - new module system only
