// domain-detection.test.mjs — verifies the 8 new v1.2 domain detectors.
//
// Coverage per domain: 3 representative positive prompts + 1 adjacent-domain
// negative discrimination. Plus cross-domain multi-fire tests (a prompt can
// hit multiple domains).
//
// Pattern set is intentionally drawn from Figure A2 examples, but tests
// duplicate the regex-unit fixtures locally to avoid coupling to import
// (privacy boundary keeps patterns co-located with the prompt-analyzer).

import { describe, it, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { runHook, setupTestDir, cleanupTestDir, createStateFile, readState } from './test-helper.mjs';

let dir;
afterEach(() => { if (dir) cleanupTestDir(dir); });

function freshState() {
  return {
    start_epoch: Math.floor(Date.now() / 1000) - 60,
    start_iso: '2026-05-01T10:00:00Z',
    tool_count: 0, edit_count: 0,
    last_event_epoch: 0, burst_count: 0,
    dep_flags: 0, esc_flags: 0, fatigue_flags: 0, val_flags: 0,
    pushback_count: 0, domain_context: null,
    last_warning_epoch: 0,
  };
}

function runPrompt(prompt, stateOverrides = {}) {
  dir = setupTestDir();
  createStateFile(dir, 'd1', { ...freshState(), ...stateOverrides });
  runHook('prompt-analyzer.mjs', { session_id: 'd1', prompt }, dir);
  return readState(dir, 'd1');
}

function assertDomainHit(s, expected) {
  assert.ok(Array.isArray(s.domain_context), `expected array, got ${typeof s.domain_context}`);
  assert.ok(s.domain_context.includes(expected),
    `expected '${expected}' in domain_context, got [${s.domain_context.join(', ')}]`);
}

function assertNoDomainHit(s, forbidden) {
  if (s.domain_context === null) return;
  assert.ok(!s.domain_context.includes(forbidden),
    `forbidden '${forbidden}' in domain_context, got [${s.domain_context.join(', ')}]`);
}

// --- Legal ---

describe('domain: legal', () => {
  it('matches "my lawyer"', () => assertDomainHit(runPrompt('I talked to my lawyer last week'), 'legal'));
  it('matches "filing a lawsuit"', () => assertDomainHit(runPrompt("we're filing a lawsuit against them"), 'legal'));
  it('matches "custody hearing"', () => assertDomainHit(runPrompt('the custody hearing is tomorrow'), 'legal'));
  it('does NOT match "lawyer joke"', () => assertNoDomainHit(runPrompt('tell me a lawyer joke'), 'legal'));
});

// --- Parenting ---

describe('domain: parenting', () => {
  it('matches "my kid"', () => assertDomainHit(runPrompt('my kid is having tantrums every morning'), 'parenting'));
  it('matches "as a parent"', () => assertDomainHit(runPrompt('as a parent I struggle with this'), 'parenting'));
  it('matches "school choice"', () => assertDomainHit(runPrompt('our school choice fight is exhausting'), 'parenting'));
  it('does NOT match "child of two parents process"', () => {
    assertNoDomainHit(runPrompt('child of two parents process in our system'), 'parenting');
  });
  it('parenting vs relationships discrimination — "my child" not "my partner"', () => {
    const s = runPrompt('my child has trouble at school');
    assertDomainHit(s, 'parenting');
    assertNoDomainHit(s, 'relationship');
  });
});

// --- Health ---

describe('domain: health', () => {
  it('matches "my doctor"', () => assertDomainHit(runPrompt('my doctor said the labs were fine'), 'health'));
  it('matches "diagnosed with"', () => assertDomainHit(runPrompt("I was diagnosed with anxiety last year"), 'health'));
  it('matches "my depression"', () => assertDomainHit(runPrompt('my depression is getting worse'), 'health'));
  it('does NOT match "system health check"', () => {
    assertNoDomainHit(runPrompt('run a system health check on the database'), 'health');
  });
  it('health vs wellbeing discrimination — generic wellbeing routine ≠ medical', () => {
    assertNoDomainHit(runPrompt('my wellbeing routine includes daily walks'), 'health');
  });
});

// --- Financial ---

describe('domain: financial', () => {
  it('matches "my retirement plan"', () => {
    assertDomainHit(runPrompt('reviewing my retirement plan strategy'), 'financial');
  });
  it('matches "mortgage application"', () => {
    assertDomainHit(runPrompt('our mortgage application got delayed'), 'financial');
  });
  it('matches "tax return"', () => {
    assertDomainHit(runPrompt("I'm working on my tax return tonight"), 'financial');
  });
  it('does NOT match "stock options trade-off in code"', () => {
    assertNoDomainHit(runPrompt('the stock options trade-off in this code'), 'financial');
  });
});

// --- Professional ---

describe('domain: professional', () => {
  it('matches "my boss"', () => assertDomainHit(runPrompt('my boss keeps changing the deadline'), 'professional'));
  it('matches "performance review"', () => assertDomainHit(runPrompt('my performance review is next week'), 'professional'));
  it('matches "resume advice"', () => assertDomainHit(runPrompt('looking for resume advice'), 'professional'));
  it('does NOT match "boss music album"', () => {
    assertNoDomainHit(runPrompt('the new Boss music album dropped'), 'professional');
  });
  it('professional vs lifepath discrimination — generic life-purpose ≠ professional', () => {
    assertNoDomainHit(runPrompt('finding my life purpose feels overwhelming'), 'professional');
  });
});

// --- Spirituality ---

describe('domain: spirituality', () => {
  it('matches "my guru"', () => assertDomainHit(runPrompt('my guru told me to meditate more'), 'spirituality'));
  it('matches "kundalini"', () => assertDomainHit(runPrompt("I've felt the kundalini rise"), 'spirituality'));
  it('matches "the universe wants"', () => {
    assertDomainHit(runPrompt('the universe wants me to take this leap'), 'spirituality');
  });
  it('does NOT match "physics universe expansion"', () => {
    assertNoDomainHit(runPrompt('how does the physics universe expansion work'), 'spirituality');
  });
});

// --- Consumer ---

describe('domain: consumer', () => {
  it('matches "should I buy"', () => assertDomainHit(runPrompt('should I buy this gaming laptop?'), 'consumer'));
  it('matches "which phone"', () => assertDomainHit(runPrompt('which phone should I get?'), 'consumer'));
  it('matches "upgrade my laptop"', () => assertDomainHit(runPrompt('time to upgrade my laptop'), 'consumer'));
  it('does NOT match "buy a property" (financial-not-consumer)', () => {
    assertNoDomainHit(runPrompt('thinking about buying a property next year'), 'consumer');
  });
});

// --- Personal_dev ---

describe('domain: personal_dev', () => {
  it('matches "my morning routine"', () => assertDomainHit(runPrompt('my morning routine needs an overhaul'), 'personal_dev'));
  it('matches "self-taught"', () => assertDomainHit(runPrompt("I'm self-taught in design"), 'personal_dev'));
  it('matches "level up myself"', () => assertDomainHit(runPrompt('want to level up myself this year'), 'personal_dev'));
  it('does NOT match "morning routine of the api"', () => {
    assertNoDomainHit(runPrompt('the morning routine of the API cron job'), 'personal_dev');
  });
});

// --- Multi-domain ---

describe('multi-domain prompts (multiple domains fire)', () => {
  it('partner + my doctor → relationship + health', () => {
    const s = runPrompt('my partner went with me to my doctor appointment');
    assertDomainHit(s, 'relationship');
    assertDomainHit(s, 'health');
  });

  it('my kid + custody hearing → parenting + legal', () => {
    const s = runPrompt('the custody hearing about my kid is next week');
    assertDomainHit(s, 'parenting');
    assertDomainHit(s, 'legal');
  });

  it('no false positive — purely technical prompt yields null domain', () => {
    const s = runPrompt('refactor this typescript module to use generics');
    assert.equal(s.domain_context, null,
      'pure tech prompt must not trigger any domain detector');
  });

  it('domain accumulates across prompts (sticky array)', () => {
    dir = setupTestDir();
    createStateFile(dir, 'd-multi', freshState());
    runHook('prompt-analyzer.mjs', { session_id: 'd-multi', prompt: 'my partner is sick' }, dir);
    runHook('prompt-analyzer.mjs', { session_id: 'd-multi', prompt: 'my doctor said to rest' }, dir);
    const s = readState(dir, 'd-multi');
    assert.ok(s.domain_context.includes('relationship'));
    assert.ok(s.domain_context.includes('health'));
    assert.equal(s.domain_context.length, 2, 'no duplicate pushes');
  });
});
