/**
 * mi6 extension for Pi Coding Agent
 *
 * This extension integrates Pi with mi6 for session monitoring.
 * It subscribes to Pi's event bus and forwards events to mi6 via shell commands.
 *
 * Installation: This file is automatically installed by `mi6 enable pi`
 * Location: ~/.pi/agent/extensions/mi6.ts (or .pi/extensions/mi6.ts for project-local)
 */

import type {
  BeforeAgentStartEvent,
  ExtensionAPI,
  ExtensionContext,
  ToolCallEvent,
  ToolResultEvent,
  TurnStartEvent,
} from "@mariozechner/pi-coding-agent";
import { execSync } from "child_process";

/**
 * Send an event to mi6 via the ingest command.
 * Uses execSync with a timeout to avoid blocking Pi for too long.
 */
function ingestEvent(eventType: string, data: Record<string, unknown>): void {
  try {
    execSync(`mi6 ingest event ${eventType} --framework pi`, {
      input: JSON.stringify(data),
      timeout: 5000,
      stdio: ["pipe", "ignore", "ignore"],
    });
  } catch {
    // Silently ignore errors - mi6 monitoring is optional
  }
}

/**
 * Get model identifier string from context.
 * Pi's model object structure varies - try multiple property access patterns.
 */
function getModelId(ctx: ExtensionContext): string | undefined {
  if (!ctx.model) return undefined;

  const model = ctx.model as Record<string, unknown>;

  // Try provider/modelId pattern (e.g., "anthropic/claude-sonnet-4-20250514")
  const provider = model.provider as string | undefined;
  const modelId = model.modelId as string | undefined;
  if (provider && modelId) {
    return `${provider}/${modelId}`;
  }

  // Try just modelId
  if (modelId) {
    return modelId;
  }

  // Try model.model (some APIs nest it)
  const nestedModel = model.model as string | undefined;
  if (nestedModel) {
    return nestedModel;
  }

  // Try model.id
  const id = model.id as string | undefined;
  if (id) {
    return id;
  }

  // Try model.name
  const name = model.name as string | undefined;
  if (name) {
    return name;
  }

  // If model is a string itself
  if (typeof ctx.model === "string") {
    return ctx.model;
  }

  return undefined;
}

/**
 * Pi extension factory function.
 * Called when Pi loads the extension.
 */
export default function (pi: ExtensionAPI): void {
  // Set environment variable for mi6 framework detection
  process.env.PI_SESSION_ID = "";

  // Session lifecycle events
  pi.on("session_start", (_event, ctx: ExtensionContext) => {
    const sessionId = ctx.sessionManager.getSessionId() ?? `pi-${Date.now()}`;
    process.env.PI_SESSION_ID = sessionId;

    // Get session file path for transcript tracking
    const sessionFile = (ctx.sessionManager as { sessionFile?: string }).sessionFile;

    ingestEvent("SessionStart", {
      session_id: sessionId,
      cwd: ctx.cwd,
      transcript_path: sessionFile,
      model: getModelId(ctx),
    });
  });

  pi.on("session_shutdown", (_event, _ctx: ExtensionContext) => {
    ingestEvent("SessionEnd", {
      session_id: process.env.PI_SESSION_ID,
    });
  });

  // Capture user prompt before agent starts
  pi.on("before_agent_start", (event: BeforeAgentStartEvent, ctx: ExtensionContext) => {
    ingestEvent("UserPromptSubmit", {
      session_id: process.env.PI_SESSION_ID,
      cwd: ctx.cwd,
      user_prompt: event.prompt,
      model: getModelId(ctx),
    });
  });

  // Tool execution events
  pi.on("tool_call", (event: ToolCallEvent, ctx: ExtensionContext) => {
    ingestEvent("PreToolUse", {
      session_id: process.env.PI_SESSION_ID,
      tool_name: event.toolName,
      tool_use_id: event.toolCallId,
      cwd: ctx.cwd,
      tool_input: event.input,
    });
  });

  pi.on("tool_result", (event: ToolResultEvent, _ctx: ExtensionContext) => {
    ingestEvent("PostToolUse", {
      session_id: process.env.PI_SESSION_ID,
      tool_name: event.toolName,
      tool_use_id: event.toolCallId,
      is_error: event.isError,
    });
  });

  // Turn tracking
  pi.on("turn_start", (event: TurnStartEvent, _ctx: ExtensionContext) => {
    // Turn start is less useful than before_agent_start for prompt capture
    // but we can track turn index
    ingestEvent("Custom", {
      session_id: process.env.PI_SESSION_ID,
      custom_type: "TurnStart",
      turn_index: event.turnIndex,
    });
  });

  pi.on("turn_end", (_event, ctx: ExtensionContext) => {
    // Try to extract token usage from the last assistant message
    let tokensInput: number | undefined;
    let tokensOutput: number | undefined;
    let tokensCacheRead: number | undefined;
    let tokensCacheWrite: number | undefined;
    let costUsd: number | undefined;

    try {
      // Get session entries and find the last assistant message with usage
      const entries = ctx.sessionManager.getBranch();
      for (let i = entries.length - 1; i >= 0; i--) {
        const entry = entries[i] as {
          type?: string;
          message?: {
            role?: string;
            usage?: {
              input?: number;
              output?: number;
              cacheRead?: number;
              cacheWrite?: number;
              totalTokens?: number;
              cost?: { total?: number };
            };
          };
        };
        if (entry.type === "message" && entry.message?.role === "assistant" && entry.message?.usage) {
          const usage = entry.message.usage;
          tokensInput = usage.input;
          tokensOutput = usage.output;
          tokensCacheRead = usage.cacheRead;
          tokensCacheWrite = usage.cacheWrite;
          costUsd = usage.cost?.total;
          break;
        }
      }
    } catch {
      // Ignore errors accessing session data
    }

    // Send ApiRequest event if we have token data, otherwise just Stop
    if (tokensInput !== undefined || tokensOutput !== undefined) {
      ingestEvent("ApiRequest", {
        session_id: process.env.PI_SESSION_ID,
        model: getModelId(ctx),
        tokens_input: tokensInput,
        tokens_output: tokensOutput,
        tokens_cache_read: tokensCacheRead,
        tokens_cache_write: tokensCacheWrite,
        cost_usd: costUsd,
      });
    }

    ingestEvent("Stop", {
      session_id: process.env.PI_SESSION_ID,
    });
  });

  // Context compaction
  pi.on("session_before_compact", (_event, _ctx: ExtensionContext) => {
    ingestEvent("PreCompact", {
      session_id: process.env.PI_SESSION_ID,
      trigger: "auto",
    });
  });
}
