import { describe, expect, it } from '@jest/globals';
import {
  buildBlockedJson,
  candidatesFromResults,
  formatBlockedMarkdown,
  type SimilarCandidate,
} from './cli-remember-similarity.js';
// `parseRememberArgs` lives in cli-remember.ts; importing it transitively
// pulls in markdown-section.ts -> mdast-util-from-markdown (pure ESM that
// ts-jest cannot transform). We exercise the parser end-to-end via the
// child-process tests in cli.test.ts instead.
import type { ScoredDocument } from './formatter.js';

describe('candidatesFromResults', () => {
  function doc(score: number, kb: string | null, relPath: string | null, content: string): ScoredDocument {
    const metadata: Record<string, unknown> = {};
    if (kb !== null) metadata.knowledgeBase = kb;
    if (relPath !== null) metadata.relativePath = relPath;
    return { pageContent: content, metadata, score };
  }

  it('strips the KB-name prefix from relativePath so suggested_invocation is reusable', () => {
    const results = [
      doc(0.42, 'ops', 'ops/runbooks/deploy.md', 'Deploy the service via the CI pipeline.'),
    ];
    const candidates = candidatesFromResults(results, 'ops');
    expect(candidates).toHaveLength(1);
    expect(candidates[0].knowledge_base).toBe('ops');
    expect(candidates[0].relative_path).toBe('runbooks/deploy.md');
    expect(candidates[0].suggested_invocation).toBe(
      'kb remember --kb=ops --append=runbooks/deploy.md --stdin --yes',
    );
  });

  it('falls back to the supplied default KB when metadata.knowledgeBase is missing', () => {
    const results = [doc(0.5, null, 'cross-kb/note.md', 'body')];
    const candidates = candidatesFromResults(results, 'fallback-kb');
    expect(candidates[0].knowledge_base).toBe('fallback-kb');
  });

  it('skips results that have no relativePath metadata (cannot build an actionable invocation)', () => {
    const results = [doc(0.4, 'ops', null, 'body')];
    expect(candidatesFromResults(results, 'ops')).toEqual([]);
  });

  it('truncates long pageContent to a bounded excerpt', () => {
    const long = 'a'.repeat(10000);
    const results = [doc(0.3, 'ops', 'ops/big.md', long)];
    const c = candidatesFromResults(results, 'ops');
    expect(c[0].chunk.length).toBeLessThan(long.length);
    expect(c[0].chunk.endsWith('…')).toBe(true);
  });

  it('preserves score verbatim from the FAISS result', () => {
    const results = [doc(0.123456, 'ops', 'ops/x.md', 'content')];
    expect(candidatesFromResults(results, 'ops')[0].score).toBeCloseTo(0.123456, 6);
  });
});

describe('buildBlockedJson', () => {
  it('produces the agent-facing JSON shape required by issue #154', () => {
    const candidates: SimilarCandidate[] = [
      {
        knowledge_base: 'operating-environment',
        relative_path: 'update-local-kb-cli-from-symlinked-checkout.md',
        score: 0.42,
        chunk: '...matching chunk text...',
        suggested_invocation: 'kb remember --kb=operating-environment --append=update-local-kb-cli-from-symlinked-checkout.md --stdin --yes',
      },
    ];
    const out = buildBlockedJson(candidates);
    expect(out.action).toBe('similarity-check');
    expect(out.write_performed).toBe(false);
    expect(out.decision_hint.summary).toMatch(/Similar KB chunks/);
    expect(out.decision_hint.recommended_agent_actions).toHaveLength(3);
    expect(out.decision_hint.recommended_agent_actions[2]).toMatch(/--force/);
    expect(out.candidates).toBe(candidates);
  });
});

describe('formatBlockedMarkdown', () => {
  it('renders header, decision hint, and one section per candidate', () => {
    const candidates: SimilarCandidate[] = [
      {
        knowledge_base: 'ops',
        relative_path: 'runbooks/deploy.md',
        score: 0.42,
        chunk: 'matched chunk text',
        suggested_invocation: 'kb remember --kb=ops --append=runbooks/deploy.md --stdin --yes',
      },
      {
        knowledge_base: 'ops',
        relative_path: 'runbooks/rollback.md',
        score: 0.71,
        chunk: 'second match',
        suggested_invocation: 'kb remember --kb=ops --append=runbooks/rollback.md --stdin --yes',
      },
    ];
    const out = formatBlockedMarkdown(candidates);
    expect(out).toContain('blocked by --check-similar guard');
    expect(out).toContain('Similar KB chunks were found before writing.');
    expect(out).toContain('## Candidate 1');
    expect(out).toContain('## Candidate 2');
    expect(out).toContain('runbooks/deploy.md');
    expect(out).toContain('Score: 0.42 (lower distance = closer match)');
    expect(out).toContain('rerun with --force');
  });
});
