// Tests for hooks/scripts/report-reader.mjs.
// Verifies aggregate computation, domain counting, and backward-compat with
// v1.0.0 records that predate pushback / domain_context fields.

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { execSync } from 'child_process';
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';

const SCRIPT = join(import.meta.dirname, '..', 'hooks', 'scripts', 'report-reader.mjs');

function runReader(jsonlContent) {
  const dir = mkdtempSync(join(tmpdir(), 'ia-report-'));
  const path = join(dir, 'sessions.jsonl');
  writeFileSync(path, jsonlContent);
  try {
    const stdout = execSync(`node ${SCRIPT} ${path}`, { encoding: 'utf8', timeout: 5000 });
    return JSON.parse(stdout.trim());
  } finally {
    rmSync(dir, { recursive: true, force: true });
  }
}

function runReaderRaw(jsonlContent) {
  const dir = mkdtempSync(join(tmpdir(), 'ia-report-'));
  const path = join(dir, 'sessions.jsonl');
  writeFileSync(path, jsonlContent);
  try {
    return execSync(`node ${SCRIPT} ${path}`, { encoding: 'utf8', timeout: 5000 });
  } finally {
    rmSync(dir, { recursive: true, force: true });
  }
}

test('pushback_total matches sum across v1.1.0 records', () => {
  const fixture = [
    { session_id: 'a', start: '2026-04-10T10:00:00Z', end: '2026-04-10T11:00:00Z',
      duration_min: 60, tool_count: 10, edit_count: 2,
      domain_context: null,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 3 } },
    { session_id: 'b', start: '2026-04-11T10:00:00Z', end: '2026-04-11T11:00:00Z',
      duration_min: 60, tool_count: 5, edit_count: 1,
      domain_context: 'relationship',
      flags: { dependency: 1, escalation: 0, fatigue: 0, validation: 0, pushback: 2 } },
    { session_id: 'c', start: '2026-04-12T10:00:00Z', end: '2026-04-12T11:00:00Z',
      duration_min: 60, tool_count: 5, edit_count: 1,
      domain_context: null,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
  ];
  const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
  const result = runReader(jsonl);
  assert.equal(result.pushback_total, 5);
  assert.equal(result.flags_total.pushback, 5);
  assert.equal(result.total_end_records, 3);
});

test('relationship_domain_count matches fixture count', () => {
  const fixture = [
    { session_id: 'a', duration_min: 30, domain_context: 'relationship',
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
    { session_id: 'b', duration_min: 30, domain_context: 'relationship',
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 1 } },
    { session_id: 'c', duration_min: 30, domain_context: null,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
    { session_id: 'd', duration_min: 30,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
  ];
  const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
  const result = runReader(jsonl);
  assert.equal(result.relationship_domain_count, 2);
  assert.equal(result.null_domain_count, 2);
});

test('v1.2 array domain_context aggregates correctly (relationship in array)', () => {
  const fixture = [
    // v1.2 — multi-domain array containing 'relationship'
    { session_id: 'a', duration_min: 30, domain_context: ['relationship', 'health'],
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 1 } },
    // v1.2 — array without 'relationship'
    { session_id: 'b', duration_min: 30, domain_context: ['legal'],
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
    // v1.2 — empty array (no domain detected this session)
    { session_id: 'c', duration_min: 30, domain_context: [],
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
    // v1.1 — string shape (must still aggregate as relationship)
    { session_id: 'd', duration_min: 30, domain_context: 'relationship',
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 1 } },
  ];
  const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
  const result = runReader(jsonl);
  assert.equal(result.relationship_domain_count, 2,
    'v1.2 array containing relationship + v1.1 string both increment relationship counter');
  assert.equal(result.other_domain_count, 1, 'v1.2 ["legal"] is "other" until Step 14 adds per-domain breakdown');
  assert.equal(result.null_domain_count, 1, 'empty array counts as null');
});

test('v1.2 mixed schema fixture: per-domain breakdown + user_info_class + valseek', () => {
  const fixture = [
    // v1.0 — no pushback flag, no domain_context
    { session_id: 'v0', duration_min: 30,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0 } },
    // v1.1 — pushback flag, string domain
    { session_id: 'v1', duration_min: 30, domain_context: 'relationship',
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 1 } },
    // v1.2 — multi-domain array, user_info_class, valseek_count
    { session_id: 'v2a', duration_min: 30,
      domain_context: ['relationship', 'health'],
      user_info_class: 'no', valseek_count: 3, turn_count: 20,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 2 } },
    { session_id: 'v2b', duration_min: 30,
      domain_context: ['legal'],
      user_info_class: 'yes_people', valseek_count: 0, turn_count: 8,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
    { session_id: 'v2c', duration_min: 30,
      domain_context: [],
      user_info_class: null, valseek_count: 0, turn_count: 5,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
  ];
  const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
  const result = runReader(jsonl);

  // schema_version discrimination
  assert.equal(result.schema_version.v1_0_records, 1);
  assert.equal(result.schema_version.v1_1_records, 1);
  assert.equal(result.schema_version.v1_2_records, 3);

  // per-domain breakdown (only v1.x array members)
  assert.equal(result.domain_breakdown.relationship, 2,
    'v1.1 string + v1.2 array containing relationship → 2');
  assert.equal(result.domain_breakdown.health, 1);
  assert.equal(result.domain_breakdown.legal, 1);
  assert.equal(result.domain_breakdown.parenting, 0);

  // user_info_class distribution
  assert.equal(result.user_info_class.no, 1);
  assert.equal(result.user_info_class.yes_people, 1);
  assert.equal(result.user_info_class.null, 1);

  // valseek aggregation
  assert.equal(result.valseek.sessions, 1);
  assert.equal(result.valseek.total, 3);

  // stakes_signal — max weight per session
  // v2a: max(relationship=1.3, health=1.5) = 1.5
  // v2b: legal=1.5
  // v2c: empty → not counted
  assert.equal(result.stakes_signal.sessions, 2);
  assert.ok(Math.abs(result.stakes_signal.sum - 3.0) < 0.01,
    `expected stakes_signal.sum ~3.0, got ${result.stakes_signal.sum}`);
});

test('backward-compat: v1.0.0 records without pushback/domain do not produce NaN', () => {
  const fixture = [
    // v1.0.0 — no pushback in flags, no domain_context at top level
    { session_id: 'old', start: '2026-03-01T10:00:00Z', end: '2026-03-01T11:00:00Z',
      duration_min: 60, tool_count: 10, edit_count: 2,
      flags: { dependency: 1, escalation: 0, fatigue: 1, validation: 0 } },
    // v1.1.0 — full schema
    { session_id: 'new', start: '2026-04-10T10:00:00Z', end: '2026-04-10T11:00:00Z',
      duration_min: 60, tool_count: 5, edit_count: 1,
      domain_context: 'relationship',
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 4 } },
    // start-only record (must be skipped)
    { session_id: 'start-only', start: '2026-04-10T09:00:00Z', hour: 9, is_late_night: false },
    // error record (must be skipped)
    { session_id: 'err', end: '2026-04-10T12:00:00Z', note: 'no_state_file' },
  ];
  const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
  const result = runReader(jsonl);
  assert.equal(result.pushback_total, 4);
  assert.equal(Number.isNaN(result.pushback_total), false);
  assert.equal(result.total_end_records, 2);
  assert.equal(result.schema_version.v1_0_records, 1);
  assert.equal(result.schema_version.v1_1_records, 1);
  assert.equal(result.flags_total.dependency, 1);
  assert.equal(result.flags_total.fatigue, 1);
});

test('report-reader stdout surfaces v1.2 field names (SC-12)', () => {
  // Run reader against a v1.2 fixture and assert stdout contains the field
  // names that /interaction-report references in its output template.
  const fixture = [
    { session_id: 'a', duration_min: 30,
      domain_context: ['legal', 'health'],
      user_info_class: 'no', valseek_count: 4, turn_count: 22,
      flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 1 } },
  ];
  const stdout = runReaderRaw(fixture.map(o => JSON.stringify(o)).join('\n') + '\n');
  // SC-12 specifies these field names must be present in the report output:
  assert.ok(stdout.includes('user_info_class'), 'stdout missing user_info_class field');
  assert.ok(stdout.includes('valseek'), 'stdout missing valseek aggregation');
  assert.ok(stdout.includes('stakes_signal'), 'stdout missing stakes_signal aggregation');
  // Also assert at least one new domain name (legal) appears in domain_breakdown.
  assert.ok(stdout.includes('legal'), 'stdout missing legal domain in breakdown');
  assert.ok(stdout.includes('domain_breakdown'), 'stdout missing domain_breakdown structure');
});
