"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var HttpInterface_exports = {};
__export(HttpInterface_exports, {
  HttpInterface: () => HttpInterface,
  JSONL_CONTENT_TYPE: () => JSONL_CONTENT_TYPE,
  JSON_CONTENT_TYPE: () => JSON_CONTENT_TYPE,
  TLV_CONTENT_TYPE: () => TLV_CONTENT_TYPE,
  UnnacceptableError: () => UnnacceptableError,
  UnsupportedRequestContentTypeError: () => UnsupportedRequestContentTypeError
});
module.exports = __toCommonJS(HttpInterface_exports);
var import_general = require("#general");
var import_types = require("#types");
var import_Api = require("../remote/api/Api.js");
var import_ApiPath = require("../remote/api/ApiPath.js");
var import_RemoteInterface = require("../remote/RemoteInterface.js");
/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("HttpInterface");
class HttpInterface extends import_RemoteInterface.RemoteInterface {
  static protocol = "http";
  #http;
  async start() {
    this.#http = await this.env.get(import_general.HttpService).create(this.address);
    this.#http.http = this.#handleRequest.bind(this);
  }
  async stop() {
    await this.#http?.close();
  }
  async #handleRequest(request) {
    let response;
    try {
      const address = new import_general.AppAddress(request.url);
      const path = this.root.subpathFor(new import_ApiPath.ApiPath(address));
      if (!path) {
        return;
      }
      response = await this.node.act("http", async (agent) => {
        const resource = await import_Api.Api.resourceFor(agent, path);
        if (resource === void 0) {
          throw new import_types.StatusResponse.NotFoundError(`Path "${address.pathname}" not found`);
        }
        return this.#applyRequestToItem(request, resource);
      });
      logSuccess(request, response);
      return response;
    } catch (e) {
      const response2 = adaptError(e);
      logError(request, response2, e);
      return response2;
    }
  }
  async #applyRequestToItem(request, item) {
    switch (request.method) {
      case "GET": {
        const responseEnv = item.read();
        if (responseEnv === void 0) {
          throw new import_general.NotImplementedError();
        }
        return adaptResponse(request, responseEnv);
      }
      case "POST": {
        const requestEnv = await adaptRequest(request);
        if (item.isInvocable) {
          const responseEnv = await item.invoke(requestEnv);
          if (responseEnv === void 0) {
            return ok();
          }
          return adaptResponse(request, responseEnv);
        }
        if (item.isSubscribable) {
          return adaptResponse(request, item.subscribe(this.abort, requestEnv));
        }
        await item.add(requestEnv);
        return ok();
      }
      case "PUT": {
        const requestPayload = await adaptRequest(request);
        item.write(requestPayload);
        return ok();
      }
      case "PATCH": {
        const requestPayload = await adaptRequest(request);
        item.write(requestPayload);
        return ok();
      }
      case "DELETE":
        await item.delete();
        break;
    }
    throw new import_general.NotImplementedError();
  }
}
class UnnacceptableError extends import_general.MatterError {
}
class UnsupportedRequestContentTypeError extends import_general.MatterError {
}
const ErrorMappings = [
  [import_types.StatusResponse.UnsupportedAccessError, 401],
  [import_types.StatusResponse.NotFoundError, 404],
  [import_general.NotImplementedError, 405],
  [UnnacceptableError, 406],
  [UnsupportedRequestContentTypeError, 415]
];
const StatusCode = {
  200: "OK",
  400: "Bad Request",
  401: "Unauthorized",
  404: "Not Found",
  405: "Method Not Allowed",
  406: "Unnacceptable",
  415: "Unsupported Media Type",
  500: "Internal Server Error"
};
const JSON_CONTENT_TYPE = "application/json";
const JSONL_CONTENT_TYPE = "application/jsonl";
const TLV_CONTENT_TYPE = "application/matter-tlv";
async function adaptRequest(request) {
  let contentType = request.headers.get("Content-Type");
  if (contentType) {
    contentType = contentType.split(";")[0].trim();
  } else {
    contentType = "application/json";
  }
  switch (contentType) {
    case JSON_CONTENT_TYPE:
      return { json: await request.text() };
    case TLV_CONTENT_TYPE:
      return { tlv: await request.bytes() };
    default:
      throw new UnsupportedRequestContentTypeError();
  }
}
function ok() {
  return new Response(null, {
    status: 200,
    statusText: StatusCode[200]
  });
}
function adaptResponse(request, response) {
  const streamingResponse = Symbol.asyncIterator in response;
  const accept = request.headers.get("Accept-Encoding");
  const encodings = accept?.split(",").map((encoding) => encoding.split(";")[0].trim());
  let contentType;
  if (encodings) {
    for (const encoding of encodings) {
      if (encoding === JSONL_CONTENT_TYPE) {
        if (streamingResponse) {
          contentType = encoding;
        } else {
          contentType = JSON_CONTENT_TYPE;
        }
        break;
      }
      if (!streamingResponse && encoding === JSON_CONTENT_TYPE || encoding === TLV_CONTENT_TYPE) {
        contentType = encoding;
        break;
      }
    }
    if (contentType === void 0) {
      throw new UnnacceptableError(`Cannot produce requested MIME type "${accept}"`);
    }
  } else if (streamingResponse) {
    contentType = JSONL_CONTENT_TYPE;
  } else {
    contentType = JSON_CONTENT_TYPE;
  }
  let body;
  if (streamingResponse) {
    body = import_general.Stream.from(EnvelopeStreamIterator(contentType, response));
  } else if (contentType === TLV_CONTENT_TYPE) {
    body = import_general.Bytes.exclusive(response.tlv);
  } else {
    body = response.json;
  }
  return new Response(
    body,
    {
      status: 200,
      statusText: StatusCode[200],
      headers: {
        "Content-Type": contentType
      }
    }
  );
}
function adaptError(e) {
  const error = (0, import_general.asError)(e);
  let status;
  for (const [type, typeStatus] of ErrorMappings) {
    if (error instanceof type) {
      status = typeStatus;
    }
  }
  if (status === void 0) {
    if (e instanceof import_types.StatusResponseError) {
      status = 400;
    } else {
      status = 500;
    }
  }
  let code, message;
  if (status >= 400 && status < 500) {
    code = error.id ?? "unknown";
    message = error.bareMessage ?? error.message;
  } else {
    code = "internal";
    message = "Internal error";
  }
  return new Response(
    JSON.stringify({
      kind: "error",
      code,
      message
    }),
    {
      status,
      statusText: StatusCode[status] ?? "Error",
      headers: {
        "Content-Type": JSON_CONTENT_TYPE,
        "Error-Code": code
      }
    }
  );
}
function logSuccess(request, response) {
  logger.notice(diagnosticHeaderFor(request, response));
}
function logError(request, response, error) {
  if (response.status >= 500 && response.status < 600) {
    logger.error(diagnosticHeaderFor(request, response), error);
  } else if (error instanceof import_general.MatterError) {
    logger.error(diagnosticHeaderFor(request, response), import_general.Diagnostic.errorMessage(error), error.message);
  } else {
    logger.warn(diagnosticHeaderFor(request, response), (0, import_general.asError)(error).message);
  }
}
function diagnosticHeaderFor(request, response) {
  return import_general.Diagnostic.squash("[", import_general.Diagnostic.strong(request.url), " ", request.method, " ", response.status, "]");
}
async function* EnvelopeStreamIterator(contentType, iterator) {
  switch (contentType) {
    case TLV_CONTENT_TYPE:
      for await (const envelope of iterator) {
        yield envelope.tlv;
      }
      break;
    case JSONL_CONTENT_TYPE:
      for await (const envelope of iterator) {
        yield `${envelope.json}
`;
      }
      break;
    default:
      throw new import_general.InternalError(`Unsupported streaming content type ${contentType}`);
  }
}
//# sourceMappingURL=HttpInterface.js.map
