// Shared constants and helper routines for session memory tools.

export const SENTINEL_START = "<!-- PRISM:AUTO-RULES:START -->";
export const SENTINEL_END = "<!-- PRISM:AUTO-RULES:END -->";

export const REDACT_PATTERNS = [
  /api_key/i, /apikey/i, /secret/i, /password/i, /token/i, /jwt/i, /auth/i, /credential/i
];

/**
 * Formats graduated insights into a markdown rules block.
 * Each insight is rendered as a bullet with its importance score,
 * event type, and the summary/correction text.
 */
export function formatRulesBlock(
  insights: Array<{ summary: string; importance: number; event_type?: string; created_at?: string }>,
  project: string
): string {
  const header = `## Prism Graduated Insights (auto-synced)\n\n` +
    `> These rules were automatically generated by [Prism MCP](https://github.com/dcostenco/prism-mcp) ` +
    `from behavioral memory for project "${project}".\n` +
    `> Last synced: ${new Date().toISOString().split("T")[0]}\n\n`;

  const rules = insights.map(i => {
    const tag = i.event_type && i.event_type !== "session" ? ` (${i.event_type})` : "";
    return `- **[importance: ${i.importance}]**${tag} ${i.summary}`;
  }).join("\n");

  return `${SENTINEL_START}\n${header}${rules}\n${SENTINEL_END}`;
}

/**
 * Idempotently replaces or appends the sentinel block in a rules file.
 * Content outside the sentinels is never modified.
 */
export function applySentinelBlock(existingContent: string, rulesBlock: string): string {
  const blockRegex = /(<!-- PRISM:AUTO-RULES:START -->)[\s\S]*?(<!-- PRISM:AUTO-RULES:END -->)/;

  if (blockRegex.test(existingContent)) {
    // Replace existing full block (non-global regex replaces the first occurrence safely)
    // Pass a replacer function to string.replace to prevent $ match-substitution vulnerabilities
    return existingContent.replace(blockRegex, () => rulesBlock);
  }

  // Fallback check: Does a partial marker exist without proper closing?
  if (existingContent.includes(SENTINEL_START) || existingContent.includes(SENTINEL_END)) {
    console.warn('[Prism] Malformed PRISM auto-rules block detected. Appending a fresh block to prevent data loss.');
    
    // Disarm orphaned sentinels to protect user content on the next run
    existingContent = existingContent
      .replaceAll(SENTINEL_START, '<!-- PRISM:AUTO-RULES:START:ORPHANED -->')
      .replaceAll(SENTINEL_END, '<!-- PRISM:AUTO-RULES:END:ORPHANED -->');
  }

  // Append with separator
  const separator = existingContent.length > 0 && !existingContent.endsWith("\n\n")
    ? (existingContent.endsWith("\n") ? "\n" : "\n\n")
    : "";
  return `${existingContent}${separator}${rulesBlock}\n`;
}

export function redactSettings(settings: Record<string, string>): Record<string, string> {
  const redacted: Record<string, string> = {};
  for (const [k, v] of Object.entries(settings || {})) {
    redacted[k] = REDACT_PATTERNS.some(p => p.test(k)) ? "**REDACTED**" : v;
  }
  return redacted;
}

export function toMarkdown(exportData: object): string {
  const data = exportData as {
    prism_export?: {
      version: string;
      exported_at: string;
      project: string;
      settings: Record<string, string>;
      handoff: unknown;
      ledger: Array<{
        id?: string;
        created_at?: string;
        event_type?: string;
        summary: string;
        todos?: string[];
        decisions?: string[];
        files_changed?: string[];
      }>;
      visual_memory: unknown[];
    };
  };
  const d = data?.prism_export;
  
  if (!d) {
    return "> **Error:** Invalid or missing Prism memory export data.";
  }

  const lines: string[] = [];

  lines.push(`# Prism Memory Export: \`${d.project || "Unknown Project"}\``);
  lines.push(``);
  lines.push(`> Exported: ${d.exported_at || "Unknown Date"}  |  Version: ${d.version || "Unknown"}`);
  lines.push(``);

  // ── Settings
  lines.push(`## ⚙️ Settings`);
  lines.push(``);
  lines.push(`| Key | Value |`);
  lines.push(`|-----|-------|`);
  for (const [k, v] of Object.entries(redactSettings(d.settings || {}))) {
    lines.push(`| \`${k}\` | ${v} |`);
  }
  lines.push(``);

  // ── Handoff State
  lines.push(`## 🎯 Live Project State (Handoff)`);
  lines.push(``);
  lines.push(`\`\`\`json`);
  lines.push(JSON.stringify(d.handoff || {}, null, 2));
  lines.push(`\`\`\``);
  lines.push(``);

  // ── Visual Memory
  if (Array.isArray(d.visual_memory) && d.visual_memory.length > 0) {
    lines.push(`## 🖼️ Visual Memory Index`);
    lines.push(``);
    for (const vm of d.visual_memory) {
      if (!vm) continue;
      const v = vm as { id?: string | number; description?: string; created_at?: string; original_filename?: string };
      const safeId = String(v.id ?? "").substring(0, 8);
      lines.push(`- **[\`${safeId}\`]** ${v.description || "No description"}`);
      lines.push(`  <small>File: \`${v.original_filename || "Unknown"}\` | Date: ${v.created_at || "Unknown"}</small>`);
    }
    lines.push(``);
  }

  // ── Ledger
  lines.push(`## 📝 Memory Ledger`);
  lines.push(``);
  if (!Array.isArray(d.ledger) || d.ledger.length === 0) {
    lines.push(`*No ledger entries found.*`);
  } else {
    for (const entry of d.ledger) {
      if (!entry) continue;
      const dateStr = typeof entry.created_at === "string" 
        ? entry.created_at.split("T")[0] 
        : "Unknown Date";
      lines.push(`### \`${dateStr}\` (Type: ${entry.event_type || "unknown"})`);
      lines.push(`**Summary:** ${entry.summary || "No summary"}`);
      if (Array.isArray(entry.todos) && entry.todos.length > 0) {
        lines.push(`\n**Outstanding TODOs:**`);
        entry.todos.forEach((t: string) => lines.push(`- [ ] ${t}`));
      }
      if (Array.isArray(entry.decisions) && entry.decisions.length > 0) {
        lines.push(`\n**Decisions:**`);
        entry.decisions.forEach((dec: string) => lines.push(`- ${dec}`));
      }
      if (Array.isArray(entry.files_changed) && entry.files_changed.length > 0) {
        lines.push(`\n**Files Changed:**`);
        entry.files_changed.forEach((f: string) => lines.push(`- \`${f}\``));
      }
      lines.push(`\n---\n`);
    }
  }

  return lines.join("\n");
}
