/**
 * test/http/digest.test.ts (W3 quick-wins c) — buildDigest read-side aggregate.
 *
 * Pure over an injected LiveSessionInfo[] (mirrors buildProjects injection):
 * counts finished (idle & lastOutputAt > since), needsInput (waiting), stuck,
 * working; sums telemetry.costUsd; empty → zeroes; future `since` → 0 finished;
 * bad `since` clamps to 0.
 */

import { describe, it, expect } from 'vitest'
import type { LiveSessionInfo, ClaudeStatus } from '../../src/types.js'
import { buildDigest, clampSince } from '../../src/http/digest.js'

function live(over: Partial<LiveSessionInfo> & { id: string; status: ClaudeStatus }): LiveSessionInfo {
  return {
    createdAt: 1000,
    clientCount: 0,
    exited: false,
    cwd: null,
    cols: 80,
    rows: 24,
    ...over,
  }
}

// ── clampSince ────────────────────────────────────────────────────────────────

describe('clampSince', () => {
  it('passes through a finite non-negative number', () => {
    expect(clampSince(1234)).toBe(1234)
    expect(clampSince(0)).toBe(0)
  })

  it('clamps NaN / negative / non-numeric / undefined to 0', () => {
    expect(clampSince(NaN)).toBe(0)
    expect(clampSince(-5)).toBe(0)
    expect(clampSince('abc')).toBe(0)
    expect(clampSince(undefined)).toBe(0)
  })

  it('parses a numeric string', () => {
    expect(clampSince('42')).toBe(42)
  })
})

// ── buildDigest ───────────────────────────────────────────────────────────────

describe('buildDigest', () => {
  it('returns an all-zero aggregate for an empty list', () => {
    const d = buildDigest([], 0)
    expect(d).toMatchObject({
      since: 0,
      total: 0,
      finished: 0,
      needsInput: 0,
      stuck: 0,
      working: 0,
      totalCostUsd: 0,
      sessions: [],
    })
    expect(typeof d.generatedAt).toBe('number')
  })

  it('counts finished (idle + output after since), needsInput, stuck, working', () => {
    const sessions: LiveSessionInfo[] = [
      live({ id: 'a', status: 'idle', lastOutputAt: 5000 }), // finished (5000 > 100)
      live({ id: 'b', status: 'waiting' }), // needsInput
      live({ id: 'c', status: 'stuck' }), // stuck
      live({ id: 'd', status: 'working' }), // working
      live({ id: 'e', status: 'idle', lastOutputAt: 50 }), // idle but stale (50 < 100) → not finished
    ]
    const d = buildDigest(sessions, 100)
    expect(d.total).toBe(5)
    expect(d.finished).toBe(1)
    expect(d.needsInput).toBe(1)
    expect(d.stuck).toBe(1)
    expect(d.working).toBe(1)
  })

  it('does not count an idle session with no lastOutputAt as finished', () => {
    const d = buildDigest([live({ id: 'a', status: 'idle' })], 0)
    expect(d.finished).toBe(0)
  })

  it('treats a future `since` as "nothing new" (0 finished)', () => {
    const d = buildDigest([live({ id: 'a', status: 'idle', lastOutputAt: 1000 })], 999_999_999_999)
    expect(d.finished).toBe(0)
  })

  it('sums telemetry.costUsd across sessions', () => {
    const sessions: LiveSessionInfo[] = [
      live({ id: 'a', status: 'idle', telemetry: { at: 1, costUsd: 1.5 } }),
      live({ id: 'b', status: 'working', telemetry: { at: 1, costUsd: 2.25 } }),
      live({ id: 'c', status: 'working' }), // no telemetry → contributes 0
    ]
    const d = buildDigest(sessions, 0)
    expect(d.totalCostUsd).toBeCloseTo(3.75)
  })

  it('projects per-session flags + title (last cwd segment)', () => {
    const d = buildDigest([live({ id: 'a', status: 'waiting', cwd: '/home/u/my-repo' })], 0)
    const row = d.sessions[0]
    expect(row?.id).toBe('a')
    expect(row?.title).toBe('my-repo')
    expect(row?.needsInput).toBe(true)
    expect(row?.finished).toBe(false)
    expect(row?.stuck).toBe(false)
  })

  it('clamps a bad `since` to 0', () => {
    const d = buildDigest([], NaN as unknown as number)
    expect(d.since).toBe(0)
  })
})
