import http from 'http';
import path from 'path';
import express from 'express';
import io from 'socket.io';
import * as uuid from 'uuid';
import { EventEmitter, ConsoleLogger } from '@microsoft/spark.common';
import { HttpPlugin, HttpStream } from '@microsoft/spark.apps';
import { router } from './routes';

class DevtoolsPlugin {
  name = "devtools";
  log;
  http;
  express;
  io;
  sockets = /* @__PURE__ */ new Map();
  httpPlugin = new HttpPlugin();
  events = new EventEmitter();
  pending = {};
  constructor() {
    this.log = new ConsoleLogger("@spark/app/devtools");
    this.express = express();
    this.http = http.createServer(this.express);
    this.io = new io.Server(this.http, { path: "/devtools/sockets" });
    this.io.on("connection", this.onConnection.bind(this));
    try {
      const dist = path.join(__dirname, "..", "devtools-web");
      this.express.use("/devtools", express.static(dist));
      this.express.get("/devtools/*", (_, res) => {
        res.sendFile(path.join(dist, "index.html"));
      });
    } catch (err) {
      this.log.warn(
        "failed to load devtools, please ensure you have installed `@microsoft/spark.devtools`"
      );
      this.log.warn(err);
    }
  }
  on(name, callback) {
    this.events.on(name, callback);
  }
  onInit(app) {
    app.event("activity.response", this.onActivityResponse.bind(this));
    app.event("activity.received", this.onActivityReceived.bind(this));
    app.event("activity.sent", this.onActivitySent.bind(this));
    const httpPlugin = app.getPlugin("http");
    if (httpPlugin && httpPlugin instanceof HttpPlugin) {
      this.httpPlugin = httpPlugin;
    }
    this.log = app.log.child("devtools");
  }
  /**
   * start listening
   * @param port port to listen on
   */
  async onStart(port = 3e3) {
    port += 1;
    this.express.use(
      router({
        port,
        log: this.log,
        process: (token, activity) => {
          return new Promise((resolve, reject) => {
            this.pending[activity.id] = { resolve, reject };
            this.events.emit("activity.received", {
              token,
              activity
            });
          });
        }
      })
    );
    return await new Promise((resolve, reject) => {
      this.http.on("error", (err) => {
        reject(err);
      });
      this.http.listen(port, async () => {
        this.log.info(`available at http://localhost:${port}/devtools`);
        resolve();
      });
    });
  }
  async onSend(activity, ref) {
    const res = await this.httpPlugin.onSend(activity, ref);
    return res;
  }
  onStreamOpen(ref) {
    return new HttpStream((activity) => {
      return this.onSend(activity, ref);
    });
  }
  onActivityReceived({ activity }) {
    this.sendActivity({
      id: uuid.v4(),
      type: "activity.received",
      chat: activity.conversation,
      body: activity,
      sentAt: /* @__PURE__ */ new Date()
    });
  }
  onActivitySent({ activity, ref }) {
    this.sendActivity({
      id: uuid.v4(),
      type: "activity.sent",
      chat: ref.conversation,
      body: activity,
      sentAt: /* @__PURE__ */ new Date()
    });
  }
  onActivityResponse({ activity, response }) {
    const promise = this.pending[activity.id];
    if (!promise) return;
    promise.resolve(response);
    delete this.pending[activity.id];
  }
  onConnection(socket) {
    this.sockets.set(socket.id, socket);
    socket.on("disconnect", () => {
      this.sockets.delete(socket.id);
    });
  }
  send(event) {
    for (const socket of this.sockets.values()) {
      socket.emit(event.type, event);
    }
  }
  sendActivity(event) {
    for (const socket of this.sockets.values()) {
      socket.emit("activity", event);
      socket.emit(event.type, event);
    }
  }
}

export { DevtoolsPlugin };
//# sourceMappingURL=plugin.mjs.map
//# sourceMappingURL=plugin.mjs.map