import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { logger } from "../monitoring/logger.js";
import { FrameError, ErrorCode } from "../errors/index.js";
class FrameStack {
  constructor(frameDb, projectId, runId) {
    this.frameDb = frameDb;
    this.projectId = projectId;
    this.runId = runId;
  }
  activeStack = [];
  /**
   * Initialize stack by loading active frames
   */
  async initialize() {
    try {
      const activeFrames = this.frameDb.getFramesByProject(
        this.projectId,
        "active"
      );
      this.activeStack = this.buildStackFromFrames(activeFrames);
      logger.info("Frame stack initialized", {
        stackDepth: this.activeStack.length,
        projectId: this.projectId
      });
    } catch (error) {
      logger.error("Failed to initialize frame stack", {
        error: error instanceof Error ? error.message : String(error),
        projectId: this.projectId,
        runId: this.runId
      });
      throw new FrameError(
        "Failed to initialize frame stack",
        ErrorCode.FRAME_INIT_FAILED,
        {
          projectId: this.projectId,
          runId: this.runId,
          originalError: error instanceof Error ? error.message : String(error)
        }
      );
    }
  }
  /**
   * Push new frame onto stack
   */
  pushFrame(frameId) {
    if (this.activeStack.includes(frameId)) {
      logger.warn("Frame already on stack", { frameId });
      return;
    }
    this.activeStack.push(frameId);
    logger.debug("Pushed frame to stack", {
      frameId,
      stackDepth: this.activeStack.length
    });
  }
  /**
   * Pop frame from stack
   */
  popFrame(frameId) {
    if (this.activeStack.length === 0) {
      return void 0;
    }
    let poppedFrameId;
    if (frameId) {
      const index = this.activeStack.indexOf(frameId);
      if (index === -1) {
        logger.warn("Frame not found on stack", { frameId });
        return void 0;
      }
      const removed = this.activeStack.splice(index);
      poppedFrameId = removed[0];
      if (removed.length > 1) {
        logger.info("Popped multiple frames due to stack unwinding", {
          targetFrame: frameId,
          removedFrames: removed
        });
      }
    } else {
      poppedFrameId = this.activeStack.pop();
    }
    if (poppedFrameId) {
      logger.debug("Popped frame from stack", {
        frameId: poppedFrameId,
        stackDepth: this.activeStack.length
      });
    }
    return poppedFrameId;
  }
  /**
   * Get current (top) frame ID
   */
  getCurrentFrameId() {
    return this.activeStack[this.activeStack.length - 1];
  }
  /**
   * Get stack depth
   */
  getDepth() {
    return this.activeStack.length;
  }
  /**
   * Get complete stack
   */
  getStack() {
    return [...this.activeStack];
  }
  /**
   * Get stack as frame objects
   */
  getStackFrames() {
    return this.activeStack.map((frameId) => this.frameDb.getFrame(frameId)).filter(Boolean);
  }
  /**
   * Get frame context for the hot stack
   */
  getHotStackContext(maxEvents = 20) {
    return this.activeStack.map((frameId) => this.buildFrameContext(frameId, maxEvents)).filter(Boolean);
  }
  /**
   * Check if frame is on stack
   */
  isFrameActive(frameId) {
    return this.activeStack.includes(frameId);
  }
  /**
   * Get parent frame ID for current frame
   */
  getParentFrameId() {
    if (this.activeStack.length < 2) {
      return void 0;
    }
    return this.activeStack[this.activeStack.length - 2];
  }
  /**
   * Get frame depth on stack (0-based)
   */
  getFrameStackDepth(frameId) {
    return this.activeStack.indexOf(frameId);
  }
  /**
   * Clear entire stack
   */
  clear() {
    const previousDepth = this.activeStack.length;
    this.activeStack = [];
    logger.info("Cleared frame stack", { previousDepth });
  }
  /**
   * Validate stack consistency
   */
  validateStack() {
    const errors = [];
    for (const frameId of this.activeStack) {
      const frame = this.frameDb.getFrame(frameId);
      if (!frame) {
        errors.push(`Frame not found in database: ${frameId}`);
        continue;
      }
      if (frame.state !== "active") {
        errors.push(
          `Frame on stack is not active: ${frameId} (state: ${frame.state})`
        );
      }
      if (frame.project_id !== this.projectId) {
        errors.push(`Frame belongs to different project: ${frameId}`);
      }
    }
    for (let i = 1; i < this.activeStack.length; i++) {
      const currentFrameId = this.activeStack[i];
      const expectedParentId = this.activeStack[i - 1];
      const currentFrame = this.frameDb.getFrame(currentFrameId);
      if (currentFrame?.parent_frame_id !== expectedParentId) {
        errors.push(
          `Frame parent mismatch: ${currentFrameId} parent should be ${expectedParentId} but is ${currentFrame?.parent_frame_id}`
        );
      }
    }
    return {
      isValid: errors.length === 0,
      errors
    };
  }
  /**
   * Build frame context for a specific frame
   */
  buildFrameContext(frameId, maxEvents) {
    try {
      const frame = this.frameDb.getFrame(frameId);
      if (!frame) {
        logger.warn("Frame not found for context building", { frameId });
        return null;
      }
      const anchors = this.frameDb.getFrameAnchors(frameId);
      const recentEvents = this.frameDb.getFrameEvents(frameId, maxEvents);
      const activeArtifacts = this.extractActiveArtifacts(recentEvents);
      return {
        frameId,
        header: {
          goal: frame.name,
          constraints: this.extractConstraints(frame.inputs),
          definitions: frame.inputs.definitions
        },
        anchors,
        recentEvents,
        activeArtifacts
      };
    } catch (error) {
      logger.warn("Failed to build frame context", { frameId, error });
      return null;
    }
  }
  /**
   * Extract constraints from frame inputs
   */
  extractConstraints(inputs) {
    const constraints = [];
    if (inputs.constraints && Array.isArray(inputs.constraints)) {
      constraints.push(...inputs.constraints);
    }
    return constraints;
  }
  /**
   * Extract active artifacts from events
   */
  extractActiveArtifacts(events) {
    const artifacts = [];
    for (const event of events) {
      if (event.event_type === "artifact" && event.payload?.path) {
        artifacts.push(event.payload.path);
      }
    }
    return [...new Set(artifacts)];
  }
  /**
   * Build stack order from database frames
   */
  buildStackFromFrames(frames) {
    if (frames.length === 0) {
      return [];
    }
    const parentMap = /* @__PURE__ */ new Map();
    const frameMap = /* @__PURE__ */ new Map();
    for (const frame of frames) {
      frameMap.set(frame.frame_id, frame);
      if (frame.parent_frame_id) {
        parentMap.set(frame.frame_id, frame.parent_frame_id);
      }
    }
    const rootFrames = frames.filter(
      (f) => !f.parent_frame_id || !frameMap.has(f.parent_frame_id)
    );
    if (rootFrames.length === 0) {
      logger.warn("No root frames found in active set");
      return [];
    }
    if (rootFrames.length > 1) {
      logger.warn("Multiple root frames found, using most recent", {
        rootFrames: rootFrames.map((f) => f.frame_id)
      });
    }
    const stack = [];
    let currentFrame = rootFrames.sort(
      (a, b) => a.created_at - b.created_at
    )[0];
    while (currentFrame) {
      stack.push(currentFrame.frame_id);
      const childFrame = frames.find(
        (f) => f.parent_frame_id === currentFrame.frame_id
      );
      if (childFrame) {
        currentFrame = childFrame;
      } else {
        break;
      }
    }
    return stack;
  }
}
export {
  FrameStack
};
//# sourceMappingURL=frame-stack.js.map
