import blessed from "blessed";
import { EventEmitter } from "events";
class FrameVisualizer extends EventEmitter {
  tree;
  // contrib.tree type
  frames;
  rootNodes;
  selectedFrame = null;
  constructor(tree) {
    super();
    this.tree = tree;
    this.frames = /* @__PURE__ */ new Map();
    this.rootNodes = [];
    this.initializeUI();
  }
  initializeUI() {
    this.tree.rows.interactive = true;
    this.tree.rows.mouse = true;
    this.tree.setData({
      extended: true,
      children: {
        Sessions: {
          extended: true,
          children: {
            "Loading...": {}
          }
        }
      }
    });
    this.tree.rows.on("select", (node) => {
      if (node && node.frameId) {
        this.selectFrame(node.frameId);
      }
    });
    this.addLegend();
  }
  addLegend() {
    const legend = `{gray-fg}Tiers: {red-fg}\u25CF Hot{/} {yellow-fg}\u25CF Warm{/} {blue-fg}\u25CF Cold{/}{/}`;
  }
  /**
   * Build tree structure from flat frame data
   */
  buildFrameTree(frames) {
    const nodeMap = /* @__PURE__ */ new Map();
    const rootNodes = [];
    frames.forEach((frame) => {
      const node = {
        id: frame.id,
        label: this.formatFrameLabel(frame),
        children: [],
        extended: frame.type === "root",
        tier: frame.tier,
        score: frame.score || 0
      };
      nodeMap.set(frame.id, node);
    });
    frames.forEach((frame) => {
      const node = nodeMap.get(frame.id);
      if (!node) return;
      if (frame.parentId) {
        const parent = nodeMap.get(frame.parentId);
        if (parent) {
          parent.children = parent.children || [];
          parent.children.push(node);
        } else {
          rootNodes.push(node);
        }
      } else {
        rootNodes.push(node);
      }
    });
    const sortNodes = (nodes) => {
      nodes.sort((a, b) => b.score - a.score);
      nodes.forEach((node) => {
        if (node.children && node.children.length > 0) {
          sortNodes(node.children);
        }
      });
    };
    sortNodes(rootNodes);
    return rootNodes;
  }
  formatFrameLabel(frame) {
    const tierIcon = this.getTierIcon(frame.tier);
    const typeIcon = this.getTypeIcon(frame.type);
    const timestamp = new Date(frame.timestamp).toLocaleTimeString();
    const tokens = frame.tokenCount ? `[${this.formatTokens(frame.tokenCount)}]` : "";
    const compression = frame.compressionRatio ? `(${frame.compressionRatio.toFixed(1)}x)` : "";
    let label = `${tierIcon} ${typeIcon} ${timestamp} ${tokens} ${compression}`;
    if (frame.digest) {
      const digest = frame.digest.length > 30 ? frame.digest.substring(0, 30) + "..." : frame.digest;
      label += ` - ${digest}`;
    }
    return label;
  }
  getTierIcon(tier) {
    switch (tier) {
      case "hot":
        return "{red-fg}\u25CF{/}";
      case "warm":
        return "{yellow-fg}\u25CF{/}";
      case "cold":
        return "{blue-fg}\u25CF{/}";
      default:
        return "{gray-fg}\u25CB{/}";
    }
  }
  getTypeIcon(type) {
    switch (type) {
      case "root":
        return "\u{1F4C1}";
      case "branch":
        return "\u{1F33F}";
      case "leaf":
        return "\u{1F343}";
      default:
        return "\u{1F4C4}";
    }
  }
  formatTokens(tokens) {
    if (tokens < 1e3) return `${tokens}`;
    if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}K`;
    return `${(tokens / 1e6).toFixed(1)}M`;
  }
  /**
   * Convert FrameNode tree to blessed-contrib tree format
   */
  convertToTreeData(nodes) {
    const children = {};
    const sessionGroups = /* @__PURE__ */ new Map();
    nodes.forEach((node) => {
      const sessionId = this.frames.get(node.id)?.sessionId || "unknown";
      if (!sessionGroups.has(sessionId)) {
        sessionGroups.set(sessionId, []);
      }
      sessionGroups.get(sessionId).push(node);
    });
    sessionGroups.forEach((sessionNodes, sessionId) => {
      const sessionLabel = `Session: ${sessionId.substring(0, 8)}`;
      children[sessionLabel] = {
        extended: true,
        children: this.buildNodeChildren(sessionNodes)
      };
    });
    const stats = this.calculateStatistics();
    children["Statistics"] = {
      extended: false,
      children: {
        [`Total Frames: ${stats.total}`]: {},
        [`Hot Tier: ${stats.hot} (${stats.hotSize})`]: {},
        [`Warm Tier: ${stats.warm} (${stats.warmSize})`]: {},
        [`Cold Tier: ${stats.cold} (${stats.coldSize})`]: {},
        [`Avg Compression: ${stats.avgCompression.toFixed(1)}x`]: {},
        [`Total Tokens: ${this.formatTokens(stats.totalTokens)}`]: {}
      }
    };
    return { extended: true, children };
  }
  buildNodeChildren(nodes) {
    const children = {};
    nodes.forEach((node) => {
      const nodeData = {
        frameId: node.id,
        extended: node.extended
      };
      if (node.children && node.children.length > 0) {
        nodeData.children = this.buildNodeChildren(node.children);
      }
      children[node.label] = nodeData;
    });
    return children;
  }
  calculateStatistics() {
    const frames = Array.from(this.frames.values());
    const stats = {
      total: frames.length,
      hot: 0,
      warm: 0,
      cold: 0,
      hotSize: "0B",
      warmSize: "0B",
      coldSize: "0B",
      totalTokens: 0,
      avgCompression: 0
    };
    let hotBytes = 0;
    let warmBytes = 0;
    let coldBytes = 0;
    let totalCompression = 0;
    let compressionCount = 0;
    frames.forEach((frame) => {
      switch (frame.tier) {
        case "hot":
          stats.hot++;
          hotBytes += frame.tokenCount * 4;
          break;
        case "warm":
          stats.warm++;
          warmBytes += frame.tokenCount * 4;
          break;
        case "cold":
          stats.cold++;
          coldBytes += frame.tokenCount * 4;
          break;
      }
      stats.totalTokens += frame.tokenCount || 0;
      if (frame.compressionRatio) {
        totalCompression += frame.compressionRatio;
        compressionCount++;
      }
    });
    stats.hotSize = this.formatBytes(hotBytes);
    stats.warmSize = this.formatBytes(warmBytes);
    stats.coldSize = this.formatBytes(coldBytes);
    stats.avgCompression = compressionCount > 0 ? totalCompression / compressionCount : 1;
    return stats;
  }
  formatBytes(bytes) {
    if (bytes < 1024) return `${bytes}B`;
    if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)}KB`;
    if (bytes < 1073741824) return `${(bytes / 1048576).toFixed(1)}MB`;
    return `${(bytes / 1073741824).toFixed(1)}GB`;
  }
  update(frames) {
    this.frames.clear();
    frames.forEach((frame) => {
      this.frames.set(frame.id, frame);
    });
    this.rootNodes = this.buildFrameTree(frames);
    const treeData = this.convertToTreeData(this.rootNodes);
    this.tree.setData(treeData);
    const stats = this.calculateStatistics();
    this.tree.screen.render();
  }
  selectFrame(frameId) {
    this.selectedFrame = frameId;
    const frame = this.frames.get(frameId);
    if (frame) {
      this.emit("frame:selected", frame);
      this.showFrameDetails(frame);
    }
  }
  showFrameDetails(frame) {
    const details = blessed.box({
      parent: this.tree.screen,
      top: "center",
      left: "center",
      width: "60%",
      height: "60%",
      content: this.formatFrameDetails(frame),
      tags: true,
      border: {
        type: "line"
      },
      style: {
        border: {
          fg: "yellow"
        }
      },
      scrollable: true,
      keys: true,
      vi: true,
      mouse: true,
      hidden: false,
      label: ` Frame: ${frame.id.substring(0, 8)} `
    });
    details.key(["escape", "q"], () => {
      details.destroy();
      this.tree.screen.render();
    });
    details.key(["m"], () => {
      this.migrateFrame(frame);
      details.destroy();
      this.tree.screen.render();
    });
    details.focus();
    this.tree.screen.render();
  }
  formatFrameDetails(frame) {
    let details = `{bold}Frame ID:{/} ${frame.id}
`;
    details += `{bold}Session:{/} ${frame.sessionId}
`;
    details += `{bold}Type:{/} ${frame.type}
`;
    details += `{bold}Tier:{/} ${this.getTierIcon(frame.tier)} ${frame.tier}
`;
    details += `{bold}Created:{/} ${new Date(frame.timestamp).toLocaleString()}
`;
    if (frame.parentId) {
      details += `{bold}Parent:{/} ${frame.parentId.substring(0, 8)}
`;
    }
    details += `
{bold}Metrics:{/}
`;
    details += `  Tokens: ${this.formatTokens(frame.tokenCount)}
`;
    details += `  Score: ${frame.score || 0}
`;
    if (frame.compressionRatio) {
      const saved = Math.round((1 - 1 / frame.compressionRatio) * 100);
      details += `  Compression: ${frame.compressionRatio.toFixed(2)}x (${saved}% saved)
`;
    }
    if (frame.digest) {
      details += `
{bold}Digest:{/}
${frame.digest}
`;
    }
    if (frame.tools && frame.tools.length > 0) {
      details += `
{bold}Tools Used:{/}
`;
      frame.tools.forEach((tool) => {
        details += `  \u2022 ${tool}
`;
      });
    }
    if (frame.inputs && frame.inputs.length > 0) {
      details += `
{bold}Inputs ({${frame.inputs.length}}):{/}
`;
      frame.inputs.slice(0, 3).forEach((input) => {
        const preview = input.length > 50 ? input.substring(0, 50) + "..." : input;
        details += `  ${preview}
`;
      });
    }
    if (frame.outputs && frame.outputs.length > 0) {
      details += `
{bold}Outputs ({${frame.outputs.length}}):{/}
`;
      frame.outputs.slice(0, 3).forEach((output) => {
        const preview = output.length > 50 ? output.substring(0, 50) + "..." : output;
        details += `  ${preview}
`;
      });
    }
    if (frame.children && frame.children.length > 0) {
      details += `
{bold}Children:{/} ${frame.children.length} frames
`;
    }
    if (frame.references && frame.references.length > 0) {
      details += `{bold}References:{/} ${frame.references.length} frames
`;
    }
    details += `
{gray-fg}[m] Migrate Tier | [d] Delete | [e] Export{/}
`;
    return details;
  }
  migrateFrame(frame) {
    const tierOrder = ["hot", "warm", "cold"];
    const currentIndex = tierOrder.indexOf(frame.tier);
    const nextTier = tierOrder[(currentIndex + 1) % tierOrder.length];
    this.emit("frame:migrate", {
      frameId: frame.id,
      fromTier: frame.tier,
      toTier: nextTier
    });
    frame.tier = nextTier;
    this.update(Array.from(this.frames.values()));
  }
  focus() {
    this.tree.rows.focus();
  }
  hasFocus() {
    return this.tree.rows === this.tree.screen.focused;
  }
}
export {
  FrameVisualizer
};
//# sourceMappingURL=frame-visualizer.js.map
