import type { OpenAPIV3 } from '@scalar/openapi-types'
import type { OAExampleObject, ParsedOpenAPI, ParsedOperation, PlaygroundSecurityScheme } from '../../types'
import { availableLanguages, useTheme } from '../../composables/useTheme'
import { buildRequest } from '../codeSamples/buildRequest'
import { generateCodeSample } from '../codeSamples/generateCodeSample'
import { resolveBaseUrl } from '../resolveBaseUrl'

export async function generateCodeSamples(spec: ParsedOpenAPI): Promise<ParsedOpenAPI> {
  if (!spec?.paths) {
    return spec
  }

  const baseUrl = resolveBaseUrl(spec.servers?.[0]?.url || '')

  for (const [path, pathObject] of Object.entries(spec.paths)) {
    for (const verb of Object.keys(pathObject) as OpenAPIV3.HttpMethods[]) {
      const operation = pathObject[verb] as ParsedOperation

      if (!operation) {
        continue
      }

      const authorizations = operation.securityUi?.[0]?.schemes || []

      const requestBodyExamples: OAExampleObject[] = Object.values(
        operation.requestBody?.content?.['application/json']?.examples || {},
      )

      const autogeneratedRequestBodyExample = requestBodyExamples
        .find(example => example?.isAutogenerated)

      const request = buildRequest({
        path,
        method: verb,
        baseUrl,
        parameters: operation.parameters || [],
        authorizations: Object.entries(authorizations).map(([name, value]: [string, any]) => {
          return {
            ...value,
            name: String(name),
            label: String(name),
          } as PlaygroundSecurityScheme
        }),
        body: autogeneratedRequestBodyExample?.value || undefined,
        headers: {
          ...(useTheme().getCodeSamplesDefaultHeaders() || {}),
        },
        variables: {},
      })

      operation.codeSamples = await Promise.all(
        availableLanguages.map(async (language) => {
          return {
            ...language,
            source: await generateCodeSample(language.lang, request),
          }
        }),
      )
    }
  }

  return spec
}
