import path from 'node:path';
import { createDebugger } from '../util/create-debugger.js';
import { CLIENT_SCRIPT_NAME } from './client-route.js';

const RESPONSE_HOOK_SYMBOL = Symbol("vite-live-preview");
const middlewareInjectInject = ({ base }) => {
  const debug = createDebugger("live-preview");
  const clientSrc = JSON.stringify(path.posix.join(base, CLIENT_SCRIPT_NAME));
  return (req, res, next) => {
    if (!req.headers.accept?.includes("html"))
      return next();
    if (RESPONSE_HOOK_SYMBOL in res)
      return next();
    Object.assign(res, { [RESPONSE_HOOK_SYMBOL]: true });
    req.headers["accept-encoding"] = "identity";
    let hooked = true;
    let restoreHead;
    const hookedUrl = req.url;
    const chunks = [];
    const writeHead = res.writeHead.bind(res);
    const write = res.write.bind(res);
    const end = res.end.bind(res);
    const push = (chunk, ...args) => {
      const encoding = args.find((arg) => typeof arg === "string");
      const callback = args.find((arg) => typeof arg === "function");
      if (typeof chunk === "string") {
        chunks.push(Buffer.from(chunk, encoding));
        callback?.();
        return true;
      }
      if (Buffer.isBuffer(chunk)) {
        chunks.push(chunk);
        callback?.();
        return true;
      }
      return false;
    };
    const restore = () => {
      if (!hooked)
        return;
      hooked = false;
      let content;
      if (chunks.length) {
        const buffer = Buffer.concat(chunks);
        chunks.length = 0;
        const text = buffer.toString("utf8");
        const injectIndex = text.search(/<\/(?:head|body|html)>/iu);
        if (injectIndex >= 0) {
          content = text.slice(0, injectIndex);
          content += `<script src=${clientSrc}><\/script>
`;
          content += text.slice(injectIndex);
          res.setHeader("Content-Length", Buffer.byteLength(content, "utf8"));
          debug?.(`injected client script into "${req.url}".`);
        } else {
          content = buffer;
          debug?.(`client script not injected into "${req.url}".`);
        }
      }
      restoreHead?.();
      restoreHead = void 0;
      if (content)
        res.write(content);
      debug?.(`unhooked html request "${hookedUrl}".`);
    };
    res.writeHead = (...args) => {
      if (!hooked)
        return writeHead(...args);
      restoreHead = () => res.writeHead(...args);
      return res;
    };
    res.write = (chunk, ...args) => {
      if (!hooked)
        return write(chunk, ...args);
      if (push(chunk, ...args))
        return true;
      debug?.(`unsupported chunk type written to "${hookedUrl}".`);
      restore?.();
      return res.write(chunk, ...args);
    };
    res.end = (...args) => {
      if (!hooked)
        return end(...args);
      if (args[0] != null && typeof args[0] !== "function") {
        res.write(...args);
      }
      restore?.();
      res.end();
      return res;
    };
    debug?.(`hooked html request "${hookedUrl}".`);
    next();
  };
};

export { middlewareInjectInject as default };
//# sourceMappingURL=client-inject.js.map
