/*
 * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
 */

import { PlexAPICore } from "../core.js";
import { encodeFormQuery, encodeSimple } from "../lib/encodings.js";
import * as M from "../lib/matchers.js";
import { compactMap } from "../lib/primitives.js";
import { safeParse } from "../lib/schemas.js";
import { RequestOptions } from "../lib/sdks.js";
import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
import { pathToFunc } from "../lib/url.js";
import {
  ConnectionError,
  InvalidRequestError,
  RequestAbortedError,
  RequestTimeoutError,
  UnexpectedClientError,
} from "../sdk/models/errors/httpclienterrors.js";
import { PlexAPIError } from "../sdk/models/errors/plexapierror.js";
import { ResponseValidationError } from "../sdk/models/errors/responsevalidationerror.js";
import { SDKValidationError } from "../sdk/models/errors/sdkvalidationerror.js";
import * as operations from "../sdk/models/operations/index.js";
import { APICall, APIPromise } from "../sdk/types/async.js";
import { Result } from "../sdk/types/fp.js";

/**
 * Get library matches
 *
 * @remarks
 * The matches endpoint is used to match content external to the library with content inside the library. This is done by passing a series of semantic "hints" about the content (its type, name, or release year). Each type (e.g. movie) has a canonical set of minimal required hints.
 * This ability to match content is useful in a variety of scenarios. For example, in the DVR, the EPG uses the endpoint to match recording rules against airing content. And in the cloud, the UMP uses the endpoint to match up a piece of media with rich metadata.
 * The endpoint response can including multiple matches, if there is ambiguity, each one containing a `score` from 0 to 100. For somewhat historical reasons, anything over 85 is considered a positive match (we prefer false negatives over false positives in general for matching).
 * The `guid` hint is somewhat special, in that it generally represents a unique identity for a piece of media (e.g. the IMDB `ttXXX`) identifier, in contrast with other hints which can be much more ambiguous (e.g. a title of `Jane Eyre`, which could refer to the 1943 or the 2011 version).
 * Episodes require either a season/episode pair, or an air date (or both). Either the path must be sent, or the show title
 */
export function libraryGetLibraryMatches(
  client: PlexAPICore,
  request: operations.GetLibraryMatchesRequest,
  options?: RequestOptions,
): APIPromise<
  Result<
    operations.GetLibraryMatchesResponse,
    | PlexAPIError
    | ResponseValidationError
    | ConnectionError
    | RequestAbortedError
    | RequestTimeoutError
    | InvalidRequestError
    | UnexpectedClientError
    | SDKValidationError
  >
> {
  return new APIPromise($do(
    client,
    request,
    options,
  ));
}

async function $do(
  client: PlexAPICore,
  request: operations.GetLibraryMatchesRequest,
  options?: RequestOptions,
): Promise<
  [
    Result<
      operations.GetLibraryMatchesResponse,
      | PlexAPIError
      | ResponseValidationError
      | ConnectionError
      | RequestAbortedError
      | RequestTimeoutError
      | InvalidRequestError
      | UnexpectedClientError
      | SDKValidationError
    >,
    APICall,
  ]
> {
  const parsed = safeParse(
    request,
    (value) => operations.GetLibraryMatchesRequest$outboundSchema.parse(value),
    "Input validation failed",
  );
  if (!parsed.ok) {
    return [parsed, { status: "invalid" }];
  }
  const payload = parsed.value;
  const body = null;

  const path = pathToFunc("/library/matches")();

  const query = encodeFormQuery({
    "grandparentTitle": payload.grandparentTitle,
    "grandparentYear": payload.grandparentYear,
    "guid": payload.guid,
    "includeAlternateMetadataSources": payload.includeAlternateMetadataSources,
    "includeAncestorMetadata": payload.includeAncestorMetadata,
    "includeFullMetadata": payload.includeFullMetadata,
    "index": payload.index,
    "originallyAvailableAt": payload.originallyAvailableAt,
    "parentIndex": payload.parentIndex,
    "parentTitle": payload.parentTitle,
    "path": payload.path,
    "title": payload.title,
    "type": payload.type,
    "year": payload.year,
  });

  const headers = new Headers(compactMap({
    Accept: "application/json",
    "X-Plex-Client-Identifier": encodeSimple(
      "X-Plex-Client-Identifier",
      payload["Client-Identifier"] ?? client._options.clientIdentifier,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Device": encodeSimple(
      "X-Plex-Device",
      payload.Device ?? client._options.device,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Device-Name": encodeSimple(
      "X-Plex-Device-Name",
      payload["Device-Name"] ?? client._options.deviceName,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Device-Vendor": encodeSimple(
      "X-Plex-Device-Vendor",
      payload["Device-Vendor"] ?? client._options.deviceVendor,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Marketplace": encodeSimple(
      "X-Plex-Marketplace",
      payload.Marketplace ?? client._options.marketplace,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Model": encodeSimple(
      "X-Plex-Model",
      payload.Model ?? client._options.model,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Platform": encodeSimple(
      "X-Plex-Platform",
      payload.Platform ?? client._options.platform,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Platform-Version": encodeSimple(
      "X-Plex-Platform-Version",
      payload["Platform-Version"] ?? client._options.platformVersion,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Product": encodeSimple(
      "X-Plex-Product",
      payload.Product ?? client._options.product,
      { explode: false, charEncoding: "none" },
    ),
    "X-Plex-Version": encodeSimple(
      "X-Plex-Version",
      payload.Version ?? client._options.version,
      { explode: false, charEncoding: "none" },
    ),
    "accepts": encodeSimple(
      "accepts",
      payload.accepts ?? client._options.accepts,
      { explode: false, charEncoding: "none" },
    ),
  }));

  const secConfig = await extractSecurity(client._options.token);
  const securityInput = secConfig == null ? {} : { token: secConfig };
  const requestSecurity = resolveGlobalSecurity(securityInput);

  const context = {
    options: client._options,
    baseURL: options?.serverURL ?? client._baseURL ?? "",
    operationID: "getLibraryMatches",
    oAuth2Scopes: null,

    resolvedSecurity: requestSecurity,

    securitySource: client._options.token,
    retryConfig: options?.retries
      || client._options.retryConfig
      || { strategy: "none" },
    retryCodes: options?.retryCodes || ["429", "500", "502", "503", "504"],
  };

  const requestRes = client._createRequest(context, {
    security: requestSecurity,
    method: "GET",
    baseURL: options?.serverURL,
    path: path,
    headers: headers,
    query: query,
    body: body,
    userAgent: client._options.userAgent,
    timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1,
  }, options);
  if (!requestRes.ok) {
    return [requestRes, { status: "invalid" }];
  }
  const req = requestRes.value;

  const doResult = await client._do(req, {
    context,
    errorCodes: ["4XX", "5XX"],
    retryConfig: context.retryConfig,
    retryCodes: context.retryCodes,
  });
  if (!doResult.ok) {
    return [doResult, { status: "request-error", request: req }];
  }
  const response = doResult.value;

  const responseFields = {
    ContentType: response.headers.get("content-type")
      ?? "application/octet-stream",
    StatusCode: response.status,
    RawResponse: response,
    Headers: {},
  };

  const [result] = await M.match<
    operations.GetLibraryMatchesResponse,
    | PlexAPIError
    | ResponseValidationError
    | ConnectionError
    | RequestAbortedError
    | RequestTimeoutError
    | InvalidRequestError
    | UnexpectedClientError
    | SDKValidationError
  >(
    M.json(200, operations.GetLibraryMatchesResponse$inboundSchema, {
      hdrs: true,
      key: "MediaContainerWithMetadata",
    }),
    M.fail("4XX"),
    M.fail("5XX"),
  )(response, req, { extraFields: responseFields });
  if (!result.ok) {
    return [result, { status: "complete", request: req, response }];
  }

  return [result, { status: "complete", request: req, response }];
}
