import { readFile } from "node:fs/promises";
import { basename } from "node:path";
import type { DatabaseAdapter } from "../db.js";
import { hmacHex } from "../privacy.js";
import type { ClientSurface } from "../sources.js";
import { isAllowedMcpServer } from "../repo-catalog.js";

interface ImportContext {
  db: DatabaseAdapter;
  clientSurface: Extract<ClientSurface, "vscode" | "vscode_insiders">;
  sourceFileId: string;
  machineId: string;
  hmacSalt: string;
}

interface ImportCounters {
  sessions_upserted: number;
  messages_upserted: number;
  runtime_events_upserted: number;
}

interface WarningEntry {
  code: string;
  message_code: string;
  severity: "warning" | "error";
  source_file_id?: string;
  details?: Record<string, number | string | boolean>;
}

interface NormalizedMessage {
  role: "user" | "assistant" | "unknown";
  model: string | null;
  created_at: string;
  tokens: {
    input: number;
    output: number;
    cache_read?: number;
    cache_write?: number;
    reasoning?: number;
  };
}

interface NormalizedToolCall {
  name: string;
  arguments: Record<string, unknown> | string;
  occurred_at: string;
}

function sanitizeWarning(input: WarningEntry): WarningEntry {
  return {
    code: input.code,
    message_code: input.code,
    severity: input.severity,
    ...(input.source_file_id ? { source_file_id: input.source_file_id } : {}),
    ...(input.details ? { details: input.details } : {})
  };
}

function cleanMode(value: unknown): "chat" | "agent" | "edit" | "unknown" {
  if (value === "chat" || value === "agent" || value === "edit") return value;
  return "unknown";
}

function cleanRole(value: unknown): "user" | "assistant" | "unknown" {
  if (value === "user" || value === "assistant") return value;
  return "unknown";
}

function cleanModel(value: unknown): string | null {
  if (typeof value !== "string") return null;
  const v = value.trim();
  if (!v || v.length > 160 || v.includes("\n") || v.includes("\\") || v.startsWith("{")) return null;
  return v;
}

export async function importVsCodeCopilotFile(
  context: ImportContext,
  absolutePath: string
): Promise<{ counters: ImportCounters; warnings: WarningEntry[] }> {
  const raw = await readFile(absolutePath, "utf8");
  const trimmedRaw = raw.trim();
  const counters: ImportCounters = { sessions_upserted: 0, messages_upserted: 0, runtime_events_upserted: 0 };
  const warnings: WarningEntry[] = [];

  let isSingleJson = false;
  let parsedObj: Record<string, any> | null = null;

  if (trimmedRaw.startsWith("{") && trimmedRaw.endsWith("}")) {
    try {
      parsedObj = JSON.parse(trimmedRaw);
      isSingleJson = true;
    } catch {
      // Fallback to line-by-line parsing
    }
  }

  // If it's a single JSON object, perform validation to verify it's a real Copilot session
  if (isSingleJson && parsedObj) {
    const v = parsedObj.v && typeof parsedObj.v === "object" ? (parsedObj.v as Record<string, any>) : null;
    const hasRequests = Array.isArray(parsedObj.requests) || (v !== null && Array.isArray(v.requests));
    const hasSessionId =
      typeof parsedObj.sessionId === "string" ||
      typeof parsedObj.session_id === "string" ||
      typeof parsedObj.id === "string" ||
      (v !== null && typeof v.id === "string");

    if (!hasRequests && !hasSessionId) {
      // Skip non-session files silently (embeddings cache, setting files, etc.)
      return { counters, warnings };
    }
  }

  const sessionsToInsert: {
    sessionId: string;
    sourceSessionHash: string;
    createdAt: string | null;
    updatedAt: string;
    mode: "chat" | "agent" | "edit" | "unknown";
    modelPrimary: string | null;
    projectId: string | null;
    messages: NormalizedMessage[];
    toolCalls: NormalizedToolCall[];
  }[] = [];

  if (isSingleJson && parsedObj) {
    const v = parsedObj.v && typeof parsedObj.v === "object" ? (parsedObj.v as Record<string, any>) : null;
    const sourceSessionId =
      (typeof parsedObj.sessionId === "string" && parsedObj.sessionId) ||
      (typeof parsedObj.session_id === "string" && parsedObj.session_id) ||
      (typeof parsedObj.id === "string" && parsedObj.id) ||
      (v !== null && typeof v.id === "string" && v.id) ||
      "unknown-session";

    const sessionId = hmacHex(`${context.machineId}:copilot:${context.clientSurface}:${sourceSessionId}`, context.hmacSalt);
    const sourceSessionHash = hmacHex(sourceSessionId, context.hmacSalt);

    const creationDate = parsedObj.creationDate || parsedObj.created_at || parsedObj.updated_at || (v && (v.creationDate || v.created_at));
    const createdAt =
      typeof creationDate === "number"
        ? new Date(creationDate).toISOString()
        : typeof creationDate === "string"
          ? creationDate
          : null;

    const lastMessageDate = parsedObj.lastMessageDate || parsedObj.updated_at || creationDate || (v && (v.lastMessageDate || v.updated_at));
    const updatedAt =
      typeof lastMessageDate === "number"
        ? new Date(lastMessageDate).toISOString()
        : typeof lastMessageDate === "string"
          ? lastMessageDate
          : new Date().toISOString();

    const requests = Array.isArray(parsedObj.requests) ? parsedObj.requests : v && Array.isArray(v.requests) ? v.requests : [];
    const messages: NormalizedMessage[] = [];
    const toolCalls: NormalizedToolCall[] = [];

    const vInputState = v?.inputState && typeof v.inputState === "object" ? v.inputState : null;
    const selectedModel = parsedObj.selectedModel || v?.selectedModel || vInputState?.selectedModel;
    const selectedMeta = selectedModel && typeof selectedModel === "object" ? selectedModel.metadata : undefined;
    const modelPrimary =
      cleanModel(parsedObj.model) ||
      cleanModel(selectedModel?.identifier) ||
      cleanModel(selectedMeta?.id) ||
      cleanModel(requests.find((r: any) => r && typeof r.modelId === "string")?.modelId);

    const parsedMessages = Array.isArray(parsedObj.messages) ? parsedObj.messages : v && Array.isArray(v.messages) ? v.messages : [];
    if (parsedMessages.length > 0) {
      for (const m of parsedMessages) {
        const tokens = (m.tokens && typeof m.tokens === "object" ? m.tokens : {}) as Record<string, unknown>;
        messages.push({
          role: cleanRole(m.role),
          model: cleanModel(m.model),
          created_at: typeof m.created_at === "string" ? m.created_at : updatedAt,
          tokens: {
            input: typeof tokens.input === "number" ? Math.max(0, Math.floor(tokens.input)) : 0,
            output: typeof tokens.output === "number" ? Math.max(0, Math.floor(tokens.output)) : 0,
            cache_read: typeof tokens.cache_read === "number" ? Math.max(0, Math.floor(tokens.cache_read)) : 0,
            cache_write: typeof tokens.cache_write === "number" ? Math.max(0, Math.floor(tokens.cache_write)) : 0,
            reasoning: typeof tokens.reasoning === "number" ? Math.max(0, Math.floor(tokens.reasoning)) : (typeof tokens.reasoning_tokens === "number" ? Math.max(0, Math.floor(tokens.reasoning_tokens)) : 0)
          }
        });
      }
    } else {
      for (const req of requests) {
        const result = req.result && typeof req.result === "object" ? req.result : {};
        const metadata = result.metadata && typeof result.metadata === "object" ? result.metadata : {};
        const inTok = typeof metadata.promptTokens === "number" ? metadata.promptTokens : 0;
        const outTok =
          typeof metadata.outputTokens === "number"
            ? metadata.outputTokens
            : typeof req.completionTokens === "number"
              ? req.completionTokens
              : 0;
        const completionTokensDetails = metadata.completionTokensDetails && typeof metadata.completionTokensDetails === "object" ? metadata.completionTokensDetails : {};
        const reasoningTok =
          typeof metadata.reasoningTokens === "number"
            ? metadata.reasoningTokens
            : (typeof completionTokensDetails.reasoningTokens === "number"
              ? completionTokensDetails.reasoningTokens
              : (typeof metadata.thinkingTokens === "number"
                ? metadata.thinkingTokens
                : 0));

        const model = cleanModel(req.modelId) || modelPrimary;

        messages.push({
          role: "user",
          model: null,
          created_at: createdAt || updatedAt,
          tokens: { input: inTok, output: 0, reasoning: 0 }
        });

        messages.push({
          role: "assistant",
          model,
          created_at: updatedAt,
          tokens: { input: 0, output: outTok, reasoning: reasoningTok }
        });

        const rounds = Array.isArray(metadata.toolCallRounds) ? metadata.toolCallRounds : [];
        for (const round of rounds) {
          const roundToolCalls = Array.isArray(round.toolCalls) ? round.toolCalls : [];
          for (const call of roundToolCalls) {
            toolCalls.push({
              name:
                typeof call.name === "string"
                  ? call.name
                  : typeof call.toolName === "string"
                    ? call.toolName
                    : typeof call.tool_name === "string"
                      ? call.tool_name
                      : "",
              arguments: call.arguments,
              occurred_at: updatedAt
            });
          }
        }
      }
    }

    const workspaceUri =
      typeof parsedObj.workspace_uri === "string"
        ? parsedObj.workspace_uri
        : v && typeof v.workspace_uri === "string"
          ? v.workspace_uri
          : undefined;
    const isRemoteWorkspace = workspaceUri !== undefined && !workspaceUri.startsWith("file://");
    const projectId =
      workspaceUri && workspaceUri.startsWith("file://")
        ? `workspace:${hmacHex(workspaceUri, context.hmacSalt).slice(0, 24)}`
        : null;

    if (isRemoteWorkspace) {
      warnings.push(
        sanitizeWarning({
          code: "REMOTE_WORKSPACE",
          message_code: "REMOTE_WORKSPACE",
          severity: "warning",
          source_file_id: context.sourceFileId
        })
      );
    }

    sessionsToInsert.push({
      sessionId,
      sourceSessionHash,
      createdAt,
      updatedAt,
      mode: cleanMode(parsedObj.mode || (v && v.mode)),
      modelPrimary,
      projectId,
      messages,
      toolCalls
    });
  } else {
    const lines = trimmedRaw.split(/\r?\n/u).map((entry) => entry.trim()).filter(Boolean);

    // Group event streams by sessionId
    const groups = new Map<
      string,
      {
        sourceSessionId: string;
        createdAt: string | null;
        updatedAt: string;
        mode: "chat" | "agent" | "edit" | "unknown";
        modelPrimary: string | null;
        workspaceUri: string | null;
        messages: NormalizedMessage[];
        toolCalls: NormalizedToolCall[];
      }
    >();

    const fileSessionId = basename(absolutePath, absolutePath.endsWith(".jsonl") ? ".jsonl" : ".json");
    let currentSessionId = fileSessionId;

    for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
      let parsed: Record<string, any>;
      try {
        parsed = JSON.parse(lines[lineIndex] ?? "") as Record<string, unknown>;
      } catch {
        continue;
      }

      const v = parsed.v && typeof parsed.v === "object" ? (parsed.v as Record<string, any>) : null;

      // Handle standard JSONL where each line represents a full session (backup format)
      if (Array.isArray(parsed.requests) || (v && Array.isArray(v.requests))) {
        const sourceSessionId =
          (typeof parsed.session_id === "string" && parsed.session_id) ||
          (typeof v?.id === "string" && v.id) ||
          `line-${lineIndex + 1}`;

        const sessionId = hmacHex(`${context.machineId}:copilot:${context.clientSurface}:${sourceSessionId}`, context.hmacSalt);
        const sourceSessionHash = hmacHex(sourceSessionId, context.hmacSalt);

        const creationDate =
          parsed.creationDate || parsed.created_at || parsed.updated_at || (v && (v.creationDate || v.created_at));
        const createdAt =
          typeof creationDate === "number"
            ? new Date(creationDate).toISOString()
            : typeof creationDate === "string"
              ? creationDate
              : null;

        const lastMessageDate =
          parsed.lastMessageDate || parsed.updated_at || creationDate || (v && (v.lastMessageDate || v.updated_at));
        const updatedAt =
          typeof lastMessageDate === "number"
            ? new Date(lastMessageDate).toISOString()
            : typeof lastMessageDate === "string"
              ? lastMessageDate
              : new Date().toISOString();

        const requests = Array.isArray(parsed.requests) ? parsed.requests : v && Array.isArray(v.requests) ? v.requests : [];
        const messages: NormalizedMessage[] = [];
        const toolCalls: NormalizedToolCall[] = [];

        const selectedModel = parsed.selectedModel || (v && v.selectedModel);
        const selectedMeta = selectedModel && typeof selectedModel === "object" ? selectedModel.metadata : undefined;
        const modelPrimary =
          cleanModel(parsed.model) ||
          cleanModel(selectedModel?.identifier) ||
          cleanModel(selectedMeta?.id) ||
          cleanModel(requests.find((r: any) => r && typeof r.modelId === "string")?.modelId);

        for (const req of requests) {
          const result = req.result && typeof req.result === "object" ? req.result : {};
          const metadata = result.metadata && typeof result.metadata === "object" ? result.metadata : {};
          const inTok = typeof metadata.promptTokens === "number" ? metadata.promptTokens : 0;
          const outTok =
            typeof metadata.outputTokens === "number"
              ? metadata.outputTokens
              : typeof req.completionTokens === "number"
                ? req.completionTokens
                : 0;
          const completionTokensDetails = metadata.completionTokensDetails && typeof metadata.completionTokensDetails === "object" ? metadata.completionTokensDetails : {};
          const reasoningTok =
            typeof metadata.reasoningTokens === "number"
              ? metadata.reasoningTokens
              : (typeof completionTokensDetails.reasoningTokens === "number"
                ? completionTokensDetails.reasoningTokens
                : (typeof metadata.thinkingTokens === "number"
                  ? metadata.thinkingTokens
                  : 0));

          const model = cleanModel(req.modelId) || modelPrimary;

          messages.push({
            role: "user",
            model: null,
            created_at: createdAt || updatedAt,
            tokens: { input: inTok, output: 0, reasoning: 0 }
          });

          messages.push({
            role: "assistant",
            model,
            created_at: updatedAt,
            tokens: { input: 0, output: outTok, reasoning: reasoningTok }
          });

          const rounds = Array.isArray(metadata.toolCallRounds) ? metadata.toolCallRounds : [];
          for (const round of rounds) {
            const roundToolCalls = Array.isArray(round.toolCalls) ? round.toolCalls : [];
            for (const call of roundToolCalls) {
              toolCalls.push({
                name:
                  typeof call.name === "string"
                    ? call.name
                    : typeof call.toolName === "string"
                      ? call.toolName
                      : typeof call.tool_name === "string"
                        ? call.tool_name
                        : "",
                arguments: call.arguments,
                occurred_at: updatedAt
              });
            }
          }
        }

        const workspaceUri =
          typeof parsed.workspace_uri === "string"
            ? parsed.workspace_uri
            : v && typeof v.workspace_uri === "string"
              ? v.workspace_uri
              : undefined;
        const isRemoteWorkspace = workspaceUri !== undefined && !workspaceUri.startsWith("file://");
        const projectId =
          workspaceUri && workspaceUri.startsWith("file://")
            ? `workspace:${hmacHex(workspaceUri, context.hmacSalt).slice(0, 24)}`
            : null;

        if (isRemoteWorkspace) {
          warnings.push(
            sanitizeWarning({
              code: "REMOTE_WORKSPACE",
              message_code: "REMOTE_WORKSPACE",
              severity: "warning",
              source_file_id: context.sourceFileId
            })
          );
        }

        sessionsToInsert.push({
          sessionId,
          sourceSessionHash,
          createdAt,
          updatedAt,
          mode: cleanMode(parsed.mode || (v && v.mode)),
          modelPrimary,
          projectId,
          messages,
          toolCalls
        });
        continue;
      }

      // Handle new JSONL event logs transcript format
      const type = parsed.type;
      const data = parsed.data && typeof parsed.data === "object" ? parsed.data : {};

      let sid = currentSessionId;
      if (type === "session.start" && typeof data.sessionId === "string") {
        sid = data.sessionId;
        currentSessionId = sid;
      } else if (typeof data.sessionId === "string") {
        sid = data.sessionId;
        currentSessionId = sid;
      } else if (typeof parsed.sessionId === "string") {
        sid = parsed.sessionId;
        currentSessionId = sid;
      }

      if (!groups.has(sid)) {
        groups.set(sid, {
          sourceSessionId: sid,
          createdAt: parsed.timestamp || null,
          updatedAt: parsed.timestamp || new Date().toISOString(),
          mode: "chat",
          modelPrimary: null,
          workspaceUri: typeof parsed.workspaceUri === "string" ? parsed.workspaceUri : null,
          messages: [],
          toolCalls: []
        });
      }

      const g = groups.get(sid)!;
      if (parsed.timestamp) {
        g.updatedAt = parsed.timestamp;
        if (!g.createdAt) g.createdAt = parsed.timestamp;
      }

      if (type === "session.start") {
        if (data.mode) g.mode = cleanMode(data.mode);
      } else if (type === "user.message") {
        g.messages.push({
          role: "user",
          model: null,
          created_at: parsed.timestamp || g.updatedAt,
          tokens: { input: 0, output: 0, reasoning: 0 }
        });
      } else if (type === "assistant.message") {
        g.messages.push({
          role: "assistant",
          model: null,
          created_at: parsed.timestamp || g.updatedAt,
          tokens: { input: 0, output: 0, reasoning: 0 }
        });

        const toolRequests = Array.isArray(data.toolRequests) ? data.toolRequests : [];
        for (const req of toolRequests) {
          g.toolCalls.push({
            name:
              typeof req.name === "string"
                ? req.name
                : typeof req.toolName === "string"
                  ? req.toolName
                  : typeof req.tool_name === "string"
                    ? req.tool_name
                    : "",
            arguments: req.arguments,
            occurred_at: parsed.timestamp || g.updatedAt
          });
        }
      } else if (type === "tool.execution_start") {
        g.toolCalls.push({
          name:
            typeof data.toolName === "string"
              ? data.toolName
              : typeof data.name === "string"
                ? data.name
                : typeof data.tool_name === "string"
                  ? data.tool_name
                  : "",
          arguments: data.arguments,
          occurred_at: parsed.timestamp || g.updatedAt
        });
      }
    }

    for (const g of groups.values()) {
      const sessionId = hmacHex(`${context.machineId}:copilot:${context.clientSurface}:${g.sourceSessionId}`, context.hmacSalt);
      const sourceSessionHash = hmacHex(g.sourceSessionId, context.hmacSalt);

      const isRemoteWorkspace = g.workspaceUri !== null && !g.workspaceUri.startsWith("file://");
      const projectId =
        g.workspaceUri && g.workspaceUri.startsWith("file://")
          ? `workspace:${hmacHex(g.workspaceUri, context.hmacSalt).slice(0, 24)}`
          : null;

      if (isRemoteWorkspace) {
        warnings.push(
          sanitizeWarning({
            code: "REMOTE_WORKSPACE",
            message_code: "REMOTE_WORKSPACE",
            severity: "warning",
            source_file_id: context.sourceFileId
          })
        );
      }

      sessionsToInsert.push({
        sessionId,
        sourceSessionHash,
        createdAt: g.createdAt,
        updatedAt: g.updatedAt,
        mode: g.mode,
        modelPrimary: g.modelPrimary,
        projectId,
        messages: g.messages,
        toolCalls: g.toolCalls
      });
    }
  }

  await context.db.exec("BEGIN IMMEDIATE TRANSACTION");
  try {
    let eventSeqGlobal = 0;
    for (const s of sessionsToInsert) {
      await context.db.run("DELETE FROM message_metrics WHERE session_id = ?", [s.sessionId]);
      await context.db.run("DELETE FROM runtime_events WHERE session_id = ?", [s.sessionId]);

      const userCount = s.messages.filter((msg) => msg.role === "user").length;
      const assistantCount = s.messages.filter((msg) => msg.role === "assistant").length;
      const tokenAvailable = s.messages.some((msg) => msg.tokens.input > 0 || msg.tokens.output > 0);

      await context.db.run(
        `
          INSERT INTO sessions(
            id, source, source_session_hash, source_file_id, machine_id, project_id, mode, model_primary, token_available,
            message_count, user_message_count, assistant_message_count, created_at, updated_at, created_at_source, client_surface, session_kind, metadata_json
          ) VALUES (?, 'copilot', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'native', ?, 'main', NULL)
          ON CONFLICT(id) DO UPDATE SET
            source_file_id = excluded.source_file_id,
            project_id = excluded.project_id,
            mode = excluded.mode,
            model_primary = COALESCE(sessions.model_primary, excluded.model_primary),
            token_available = excluded.token_available,
            message_count = excluded.message_count,
            user_message_count = excluded.user_message_count,
            assistant_message_count = excluded.assistant_message_count,
            created_at = excluded.created_at,
            updated_at = excluded.updated_at,
            client_surface = excluded.client_surface,
            session_kind = excluded.session_kind
        `,
        [
          s.sessionId,
          s.sourceSessionHash,
          context.sourceFileId,
          context.machineId,
          s.projectId,
          s.mode,
          s.modelPrimary,
          tokenAvailable ? 1 : 0,
          s.messages.length,
          userCount,
          assistantCount,
          s.createdAt,
          s.updatedAt,
          context.clientSurface
        ]
      );
      counters.sessions_upserted += 1;

      for (let idx = 0; idx < s.messages.length; idx += 1) {
        const msg = s.messages[idx]!;
        const msgId = hmacHex(`${context.machineId}:${s.sessionId}:${idx + 1}`, context.hmacSalt);
        const msgTokenAvailable = msg.tokens.input > 0 || msg.tokens.output > 0 || (msg.tokens.reasoning ?? 0) > 0;

        await context.db.run(
          `
            INSERT INTO message_metrics(
              id, session_id, source_file_id, seq, role, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
              reasoning_tokens, token_available, partial_token_data, created_at, timestamp_source, metadata_json
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, 'native', NULL)
          `,
          [
            msgId,
            s.sessionId,
            context.sourceFileId,
            idx + 1,
            msg.role,
            msg.model,
            msg.tokens.input,
            msg.tokens.output,
            msg.tokens.cache_read ?? 0,
            msg.tokens.cache_write ?? 0,
            msg.tokens.reasoning ?? 0,
            msgTokenAvailable ? 1 : 0,
            msg.created_at
          ]
        );
        counters.messages_upserted += 1;
      }

      for (const call of s.toolCalls) {
        const rawName = call.name;
        const m = /^mcp__([a-z0-9_]+)__([a-zA-Z0-9_]+)$/u.exec(rawName);
        if (!m) continue;
        const mcpServerName = m[1].replaceAll("_", "-");
        const toolName = m[2];
        if (!isAllowedMcpServer(mcpServerName)) continue;
        const eventName = `${mcpServerName}.${toolName}`;
        const occurredAt = call.occurred_at;

        let args: Record<string, unknown> = {};
        if (typeof call.arguments === "string") {
          try {
            args = JSON.parse(call.arguments);
          } catch {}
        } else if (call.arguments && typeof call.arguments === "object") {
          args = call.arguments as Record<string, unknown>;
        }

        const keys = Object.keys(args).filter((k) => /^[a-zA-Z0-9_]{1,80}$/u.test(k)).sort();
        const argsKeysJson = JSON.stringify(keys);
        const argsJson = JSON.stringify(args);
        const argsHash = hmacHex(argsJson, context.hmacSalt);
        const argsBytes = Buffer.byteLength(argsJson, "utf8");
        const eventId = hmacHex(
          `${context.machineId}:canon:mcp:${eventName}:${occurredAt}:${s.sessionId}:${argsHash || "noargs"}`,
          context.hmacSalt
        );
        eventSeqGlobal += 1;

        await context.db.run(
          `
            INSERT OR IGNORE INTO runtime_events(
              id, source, source_file_id, session_id, event_type, event_origin, event_name, mcp_server_name, tool_name,
              skill_name, hook_name, hook_event, source_line_no, event_seq, args_keys_json, args_hash, args_bytes,
              occurred_at, timestamp_source, is_self_event, metadata_json
            ) VALUES (?, 'copilot', ?, ?, 'mcp', 'observed_structured', ?, ?, ?, NULL, NULL, NULL, NULL, ?, ?, ?, ?, ?, 'native', ?, NULL)
          `,
          [
            eventId,
            context.sourceFileId,
            s.sessionId,
            eventName,
            mcpServerName,
            toolName,
            eventSeqGlobal,
            argsKeysJson,
            argsHash,
            argsBytes,
            occurredAt,
            mcpServerName === "analytics-mcp-server" ? 1 : 0
          ]
        );

        const observationId = hmacHex(`${eventId}:copilot:${context.sourceFileId}:${eventSeqGlobal}:chat_structured`, context.hmacSalt);
        await context.db.run(
          `INSERT OR IGNORE INTO runtime_event_observations(
            id, runtime_event_id, observed_source, source_file_id, session_id, source_line_no, event_seq, observation_kind, observed_at
          ) VALUES (?, ?, 'copilot', ?, ?, NULL, ?, 'chat_structured', ?)`,
          [observationId, eventId, context.sourceFileId, s.sessionId, eventSeqGlobal, occurredAt]
        );
        counters.runtime_events_upserted += 1;
      }
    }
    await context.db.exec("COMMIT");
  } catch (error) {
    await context.db.exec("ROLLBACK");
    throw error;
  }

  return { counters, warnings };
}
