import { describe, it, expect } from 'vitest'
import { computeMemoryStaleness, formatMemoryManifest, type MemoryHeader } from '../memoryScan.js'

const DAY = 24 * 60 * 60 * 1000

function header(overrides: Partial<MemoryHeader>): MemoryHeader {
  return {
    filename: 'note.md',
    filePath: '/tmp/note.md',
    mtimeMs: Date.now(),
    description: 'a note',
    type: 'project',
    ...overrides,
  }
}

describe('computeMemoryStaleness', () => {
  const NOW = Date.UTC(2026, 3, 17, 12, 0, 0)

  it('returns "fresh" for memories under 30 days', () => {
    expect(computeMemoryStaleness(NOW - 5 * DAY, NOW)).toBe('fresh')
    expect(computeMemoryStaleness(NOW - 29 * DAY, NOW)).toBe('fresh')
  })

  it('returns "stale" for memories 30–89 days old', () => {
    expect(computeMemoryStaleness(NOW - 30 * DAY, NOW)).toBe('stale')
    expect(computeMemoryStaleness(NOW - 60 * DAY, NOW)).toBe('stale')
    expect(computeMemoryStaleness(NOW - 89 * DAY, NOW)).toBe('stale')
  })

  it('returns "very-stale" for memories 90+ days old', () => {
    expect(computeMemoryStaleness(NOW - 90 * DAY, NOW)).toBe('very-stale')
    expect(computeMemoryStaleness(NOW - 365 * DAY, NOW)).toBe('very-stale')
  })

  it('treats future mtimes as fresh (clock skew tolerance)', () => {
    expect(computeMemoryStaleness(NOW + DAY, NOW)).toBe('fresh')
  })
})

describe('formatMemoryManifest staleness tagging', () => {
  const NOW = Date.UTC(2026, 3, 17, 12, 0, 0)

  it('does not tag fresh memories', () => {
    const out = formatMemoryManifest(
      [header({ mtimeMs: NOW - 5 * DAY, description: 'recent' })],
      NOW,
    )
    expect(out).not.toMatch(/stale/i)
    expect(out).toContain('recent')
  })

  it('tags stale memories (30–89 days) with [stale]', () => {
    const out = formatMemoryManifest(
      [header({ mtimeMs: NOW - 45 * DAY, description: 'middle-aged' })],
      NOW,
    )
    expect(out).toContain('[stale]')
    expect(out).not.toContain('[very-stale]')
  })

  it('tags very-stale memories (90+ days) with [very-stale, verify before trusting]', () => {
    const out = formatMemoryManifest(
      [header({ mtimeMs: NOW - 120 * DAY, description: 'ancient' })],
      NOW,
    )
    expect(out).toContain('[very-stale')
    expect(out).toMatch(/verify before trusting/i)
  })

  it('preserves the existing type tag and description format', () => {
    const out = formatMemoryManifest(
      [
        header({
          mtimeMs: NOW - 10 * DAY,
          description: 'a note',
          type: 'feedback',
        }),
      ],
      NOW,
    )
    expect(out).toContain('[feedback]')
    expect(out).toContain('a note')
  })

  it('handles multiple memories with mixed staleness', () => {
    const out = formatMemoryManifest(
      [
        header({
          filename: 'fresh.md',
          mtimeMs: NOW - 1 * DAY,
          description: 'fresh',
        }),
        header({
          filename: 'stale.md',
          mtimeMs: NOW - 45 * DAY,
          description: 'stale',
        }),
        header({
          filename: 'very.md',
          mtimeMs: NOW - 200 * DAY,
          description: 'very stale',
        }),
      ],
      NOW,
    )
    const lines = out.split('\n')
    expect(lines).toHaveLength(3)
    expect(lines[0]).not.toMatch(/stale/i)
    expect(lines[1]).toContain('[stale]')
    expect(lines[2]).toContain('[very-stale')
  })

  it('defaults `now` to real time when not passed', () => {
    const out = formatMemoryManifest([
      header({ mtimeMs: Date.now() - 5 * DAY, description: 'recent' }),
    ])
    expect(out).not.toMatch(/stale/i)
  })
})
