// Generated by smithplates HTTP codegen. Do not edit.

export interface AuthCredential {
  schemeId: string;
  value: string;
}

export interface AuthProvider {
  resolveAuth(schemeId: string): AuthCredential | null | undefined;
}

export interface AuthSchemeBinding {
  schemeId: string;
  location: "header" | "query" | "cookie";
  name: string;
  prefix: string | null;
}

export interface OperationAuthBinding {
  alternatives: string[];
  allowsAnonymous: boolean;
}

export const AUTH_SCHEME_BINDINGS: Record<string, AuthSchemeBinding> = {
  "smithplates.codegen.http#httpCookieAuth": {
    schemeId: "smithplates.codegen.http#httpCookieAuth",
    location: "cookie",
    name: "session",
    prefix: null,
  },
  "smithy.api#httpBearerAuth": {
    schemeId: "smithy.api#httpBearerAuth",
    location: "header",
    name: "Authorization",
    prefix: "Bearer",
  },
  "smithy.api#httpApiKeyAuth": {
    schemeId: "smithy.api#httpApiKeyAuth",
    location: "header",
    name: "X-API-Key",
    prefix: "ApiKey",
  },
};

export const OPERATION_AUTH_BINDINGS: Record<string, OperationAuthBinding> = {
  "apiKeyOnly": {
    alternatives: [
      "smithy.api#httpApiKeyAuth",
    ],
    allowsAnonymous: false,
  },
  "ApiKeyOnly": {
    alternatives: [
      "smithy.api#httpApiKeyAuth",
    ],
    allowsAnonymous: false,
  },
  "bearerOnly": {
    alternatives: [
      "smithy.api#httpBearerAuth",
    ],
    allowsAnonymous: false,
  },
  "BearerOnly": {
    alternatives: [
      "smithy.api#httpBearerAuth",
    ],
    allowsAnonymous: false,
  },
  "cookieOnly": {
    alternatives: [
      "smithplates.codegen.http#httpCookieAuth",
    ],
    allowsAnonymous: false,
  },
  "CookieOnly": {
    alternatives: [
      "smithplates.codegen.http#httpCookieAuth",
    ],
    allowsAnonymous: false,
  },
  "optional": {
    alternatives: [
      "smithplates.codegen.http#httpCookieAuth",
      "smithy.api#httpBearerAuth",
      "smithy.api#httpApiKeyAuth",
      "smithy.api#noAuth",
    ],
    allowsAnonymous: true,
  },
  "Optional": {
    alternatives: [
      "smithplates.codegen.http#httpCookieAuth",
      "smithy.api#httpBearerAuth",
      "smithy.api#httpApiKeyAuth",
      "smithy.api#noAuth",
    ],
    allowsAnonymous: true,
  },
  "public_": {
    alternatives: [
      "smithy.api#noAuth",
    ],
    allowsAnonymous: true,
  },
  "Public": {
    alternatives: [
      "smithy.api#noAuth",
    ],
    allowsAnonymous: true,
  },
  "required": {
    alternatives: [
      "smithplates.codegen.http#httpCookieAuth",
      "smithy.api#httpBearerAuth",
      "smithy.api#httpApiKeyAuth",
    ],
    allowsAnonymous: false,
  },
  "Required": {
    alternatives: [
      "smithplates.codegen.http#httpCookieAuth",
      "smithy.api#httpBearerAuth",
      "smithy.api#httpApiKeyAuth",
    ],
    allowsAnonymous: false,
  },
};

function encodeRfc3986(value: string): string {
  return encodeURIComponent(value).replace(
    /[!'()*]/g,
    (character) => "%" + character.charCodeAt(0).toString(16).toUpperCase(),
  );
}

function setHeader(headers: Record<string, string>, name: string, value: string): void {
  const existingKey = Object.keys(headers).find((key) => key.toLowerCase() === name.toLowerCase());
  if (existingKey !== undefined && existingKey !== name) {
    delete headers[existingKey];
  }
  headers[name] = value;
}

export function applyOperationAuth(
  binding: OperationAuthBinding,
  authProvider: AuthProvider | undefined,
  headers: Record<string, string>,
  queryParts: string[],
): void {
  let browserCookieAvailable = false;
  const allowedSchemeIds = new Set(
    binding.alternatives.filter((schemeId) => schemeId !== "smithy.api#noAuth"),
  );
  for (const schemeId of binding.alternatives) {
    if (schemeId === "smithy.api#noAuth") {
      return;
    }

    const scheme = AUTH_SCHEME_BINDINGS[schemeId];
    if (scheme === undefined) {
      throw new Error("No auth binding registered for scheme " + schemeId);
    }
    if (scheme.location === "cookie") {
      browserCookieAvailable = true;
      continue;
    }
    if (authProvider === undefined) {
      continue;
    }

    const credential = authProvider.resolveAuth(schemeId);
    if (credential === null || credential === undefined) {
      continue;
    }
    if (!allowedSchemeIds.has(credential.schemeId)) {
      throw new Error(
        "Auth provider returned scheme " + credential.schemeId + ", which is not allowed by the operation",
      );
    }
    if (credential.schemeId !== schemeId) {
      throw new Error(
        "Auth provider returned scheme " + credential.schemeId + " while resolving " + schemeId,
      );
    }
    if (credential.value.length === 0) {
      continue;
    }

    const value = scheme.prefix === null ? credential.value : scheme.prefix + " " + credential.value;
    if (scheme.location === "header") {
      setHeader(headers, scheme.name, value);
    } else if (scheme.location === "query") {
      queryParts.push(encodeRfc3986(scheme.name) + "=" + encodeRfc3986(value));
    }
    return;
  }

  if (browserCookieAvailable) {
    return;
  }
  if (!binding.allowsAnonymous) {
    throw new Error("No usable authentication credential is available for the operation");
  }
}

export interface ResponseVariantBinding {
  statusCode: number;
  mediaType: string | null;
  headerBindings: Array<[string, string]>;
  staticHeaders: Array<[string, string]>;
}

export interface ClientOperationHttpBinding {
  variantsByTypeName: Record<string, ResponseVariantBinding>;
  variantsByStatus: Record<number, string>;
}

/** Look up the response variant registered for a given HTTP status code. */
export function variantForStatus(
  binding: ClientOperationHttpBinding,
  statusCode: number,
): ResponseVariantBinding {
  const typeName = binding.variantsByStatus[statusCode];
  if (typeName === undefined) {
    throw new Error("No HTTP binding registered for response status " + statusCode);
  }
  const variant = binding.variantsByTypeName[typeName];
  if (variant === undefined) {
    throw new Error("No HTTP binding registered for response type " + typeName);
  }
  return variant;
}

/** Look up the response model type name registered for a given HTTP status code. */
export function typeNameForStatus(
  binding: ClientOperationHttpBinding,
  statusCode: number,
): string {
  const typeName = binding.variantsByStatus[statusCode];
  if (typeName === undefined) {
    throw new Error("No HTTP binding registered for response status " + statusCode);
  }
  return typeName;
}

export const OPERATION_HTTP_BINDINGS: Record<string, ClientOperationHttpBinding> = {
  "apiKeyOnly": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "ApiKeyOnly": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "bearerOnly": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "BearerOnly": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "cookieOnly": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "CookieOnly": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "optional": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "Optional": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "public_": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "Public": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "required": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
  "Required": {
    variantsByTypeName: {
      "AuthOutput": {
        statusCode: 200,
        mediaType: "application/json",
        headerBindings: [],
        staticHeaders: [],
      },
    },
    variantsByStatus: {
      200: "AuthOutput",
    },
  },
};
