#!/usr/bin/env node
/**
 * Local Streamable HTTP entrypoint — a testing stand-in for the hosted
 * mcp.westrockappraisal.com server described in doc 14. Same tools, same
 * code, just reachable over HTTP/localhost (or the LAN) instead of stdio,
 * so the HTTP connection pattern can be tried before a real deploy.
 *
 * This is NOT the OAuth design from doc 14 — it's a single shared bearer
 * token for short-lived local testing only. Rotate/remove the token file
 * when testing is done; do not point it at the internet as-is.
 */
import http from "node:http";
import crypto from "node:crypto";
import fs from "node:fs";
import { randomUUID } from "node:crypto";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { createWestrockServer } from "./server.js";

const PORT = Number(process.env.PORT ?? 8787);
const HOST = process.env.HOST ?? "127.0.0.1";
const TOKEN_FILE = process.env.WESTROCK_MCP_TOKEN_FILE;
const INCLUDE_ZCLIENTS = process.env.WESTROCK_MCP_INCLUDE_ZCLIENTS === "1";

function getOrCreateToken(): string {
  if (process.env.WESTROCK_MCP_TOKEN) return process.env.WESTROCK_MCP_TOKEN;
  if (!TOKEN_FILE) {
    throw new Error(
      "Set WESTROCK_MCP_TOKEN or WESTROCK_MCP_TOKEN_FILE before starting the HTTP server."
    );
  }
  if (fs.existsSync(TOKEN_FILE)) {
    return fs.readFileSync(TOKEN_FILE, "utf-8").trim();
  }
  const token = crypto.randomBytes(24).toString("hex");
  fs.writeFileSync(TOKEN_FILE, token, { encoding: "utf-8", mode: 0o600 });
  return token;
}

const TOKEN = getOrCreateToken();

type Session = {
  transport: StreamableHTTPServerTransport;
};

const sessions = new Map<string, Session>();

function checkAuth(req: http.IncomingMessage): boolean {
  const header = req.headers["authorization"];
  if (!header || Array.isArray(header)) return false;
  const match = header.match(/^Bearer\s+(.+)$/i);
  if (!match) return false;
  const given = Buffer.from(match[1]);
  const expected = Buffer.from(TOKEN);
  if (given.length !== expected.length) return false;
  return crypto.timingSafeEqual(given, expected);
}

const server = http.createServer(async (req, res) => {
  if (req.url !== "/mcp") {
    res.writeHead(404).end("Not found");
    return;
  }

  if (!checkAuth(req)) {
    res.writeHead(401, { "Content-Type": "application/json" }).end(
      JSON.stringify({ error: "Missing or invalid Authorization: Bearer <token>" })
    );
    return;
  }

  const sessionId = req.headers["mcp-session-id"] as string | undefined;

  let session = sessionId ? sessions.get(sessionId) : undefined;

  if (!session) {
    const mcpServer = createWestrockServer({ includeZclients: INCLUDE_ZCLIENTS });
    const transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: () => randomUUID(),
      onsessioninitialized: (newId) => {
        sessions.set(newId, { transport });
      },
    });
    transport.onclose = () => {
      if (transport.sessionId) sessions.delete(transport.sessionId);
    };
    await mcpServer.connect(transport);
    session = { transport };
  }

  await session.transport.handleRequest(req, res);
});

server.listen(PORT, HOST, () => {
  console.log(`westrock MCP (HTTP) listening on http://${HOST}:${PORT}/mcp`);
  console.log(`zclients tools: ${INCLUDE_ZCLIENTS ? "included" : "excluded (not reachable from this host)"}`);
  console.log(
    "Auth: single shared bearer token, not the doc-14 OAuth design. " +
      "Treat the token file as a secret."
  );
});
