import { IHttpRequest, Kind as HttpRequestKind } from '../models/HttpRequest.js'
import { IRequestAuthorization } from '../models/RequestAuthorization.js'
import { IRequestConfig } from '../models/RequestConfig.js'
import Proxy, { IProxyResult } from './Proxy.js'
import { HttpCertificate } from '../models/ClientCertificate.js'
import { HttpRequestRunner } from '../runtime/http-runner/HttpRequestRunner.js'
import { IRequestLog } from '../models/RequestLog.js'
import { IHttpFlow } from '../models/http-flows/HttpFlows.js'
import { IHttpAssertion } from '../models/http-flows/HttpAssertion.js'
import { Exception } from '../exceptions/exception.js'
import { createLogger } from '../lib/logging/index.js'

export interface IRequestProxyInit {
  kind: typeof HttpRequestKind
  /**
   * The request to execute.
   */
  request: IHttpRequest
  /**
   * The authorization data to apply.
   */
  authorization?: IRequestAuthorization[]
  /**
   * The request configuration.
   */
  config?: IRequestConfig
  /**
   * The list of execution variables to use with the request.
   */
  variables?: Record<string, string>
  /**
   * The certificate data to use with the request.
   */
  certificate?: HttpCertificate
  /**
   * The request flows to execute with the request.
   */
  flows?: IHttpFlow[]
  /**
   * Assertions to execute with the request.
   */
  assertions?: IHttpAssertion[]
  /**
   * A signal to be sent to the proxy bindings to abort the request on demand.
   */
  signal?: AbortSignal | null
}

/**
 * Proxies a single HTTP request
 */
export default class RequestProxy extends Proxy {
  init?: IRequestProxyInit

  async configure(init: IRequestProxyInit): Promise<void> {
    const { request } = init
    const INVALID_REQUEST = 'Invalid request'
    const code = 'E_INVALID_REQUEST'
    if (!request) {
      throw new Exception(INVALID_REQUEST, {
        help: 'The "request" parameter is required.',
        status: 400,
        code,
      })
    }
    if (!request.url) {
      throw new Exception(INVALID_REQUEST, {
        help: 'The "request.url" parameter is required.',
        status: 400,
        code,
      })
    }
    this.init = init
  }

  async execute(): Promise<IProxyResult<IRequestLog>> {
    const data = this.init as IRequestProxyInit
    const { request, authorization, certificate, config = {}, variables = {}, flows, assertions } = data
    const factory = new HttpRequestRunner()
    factory.variables = variables
    factory.logger = createLogger()
    factory.config = { ...config, enabled: true } as IRequestConfig
    if (Array.isArray(authorization) && authorization.length) {
      factory.authorization = authorization
    }
    if (Array.isArray(flows) && flows.length) {
      factory.flows = flows
    }
    if (Array.isArray(assertions) && assertions.length) {
      factory.assertions = assertions
    }
    if (certificate) {
      factory.certificates = [certificate]
    }
    const result = await factory.run(request)
    return {
      result: result,
      variables,
    }
  }
}
