import { describe, expect, it } from 'vitest';
import {
  detectEnforcementHooksWired,
  runLeaseEnforcementDoctorCheck,
} from './lease-doctor-check.js';

/**
 * Red-first tests for issue #869 Point-1 C5 — the `mosaic doctor`
 * lease-enforcement surfacing check.
 *
 * Root cause under test: enforcement hooks (`mutator-gate.py`,
 * `receipt-observer-client.py`) can be wired into `~/.claude/settings.json`
 * on a host where C1's `leaseEnforcementActivatable()` is false and/or C3's
 * `checkBrokerSupervisorHealth()` reports unhealthy. That combination fails
 * closed correctly, but must be surfaced LOUDLY by `mosaic doctor` rather
 * than silently passing — this test suite exercises the three primary
 * branches (wired+not-activatable, wired+healthy, not-wired) plus the
 * broker-unhealthy variant.
 *
 * Every dependency is injected — no real `~/.claude/settings.json` and no
 * real broker are ever touched.
 */

const WIRED_SETTINGS_JSON = JSON.stringify({
  hooks: {
    PreToolUse: [
      {
        matcher: '.*',
        hooks: [
          {
            type: 'command',
            command: 'python3 ~/.config/mosaic/tools/lease-broker/mutator-gate.py --runtime claude',
          },
        ],
      },
    ],
    Stop: [
      {
        hooks: [
          {
            type: 'command',
            command:
              'python3 ~/.config/mosaic/tools/lease-broker/receipt-observer-client.py --runtime claude --latest-entry',
          },
        ],
      },
    ],
  },
});

const UNWIRED_SETTINGS_JSON = JSON.stringify({
  hooks: {
    PostToolUse: [
      {
        matcher: 'Edit|MultiEdit|Write',
        hooks: [{ type: 'command', command: '~/.config/mosaic/tools/qa/qa-hook-stdin.sh' }],
      },
    ],
  },
});

describe('detectEnforcementHooksWired', () => {
  it('detects the mutator-gate + receipt-observer markers when wired', () => {
    const result = detectEnforcementHooksWired(JSON.parse(WIRED_SETTINGS_JSON));
    expect(result.wired).toBe(true);
    expect(result.matchedMarkers).toEqual(
      expect.arrayContaining(['mutator-gate.py', 'receipt-observer-client.py']),
    );
  });

  it('reports not wired when no enforcement markers are present', () => {
    const result = detectEnforcementHooksWired(JSON.parse(UNWIRED_SETTINGS_JSON));
    expect(result.wired).toBe(false);
    expect(result.matchedMarkers).toEqual([]);
  });

  it('reports not wired for an empty settings object', () => {
    expect(detectEnforcementHooksWired({}).wired).toBe(false);
  });

  it('detects wiring from just ONE marker (partial wiring is still dangerous)', () => {
    const onlyMutatorGate = JSON.stringify({
      hooks: {
        PreToolUse: [
          {
            hooks: [{ type: 'command', command: 'python3 .../mutator-gate.py --runtime claude' }],
          },
        ],
      },
    });
    const result = detectEnforcementHooksWired(JSON.parse(onlyMutatorGate));
    expect(result.wired).toBe(true);
    expect(result.matchedMarkers).toEqual(['mutator-gate.py']);
  });
});

describe('runLeaseEnforcementDoctorCheck', () => {
  it('RED: wired + not-activatable ⇒ LOUD error (not a silent pass)', async () => {
    const result = await runLeaseEnforcementDoctorCheck({
      readSettingsRaw: () => WIRED_SETTINGS_JSON,
      isActivatable: () => false,
      isBrokerHealthy: async () => true,
    });

    expect(result.status).toBe('error');
    expect(result.wired).toBe(true);
    expect(result.activatable).toBe(false);
    expect(result.message).toMatch(/activation absent/);
    expect(result.message).toMatch(/#869/);
    expect(result.message.toLowerCase()).toMatch(/brick/);
  });

  it('wired + activatable + broker-unhealthy ⇒ LOUD error', async () => {
    const result = await runLeaseEnforcementDoctorCheck({
      readSettingsRaw: () => WIRED_SETTINGS_JSON,
      isActivatable: () => true,
      isBrokerHealthy: async () => false,
    });

    expect(result.status).toBe('error');
    expect(result.wired).toBe(true);
    expect(result.brokerHealthy).toBe(false);
    expect(result.message).toMatch(/broker not healthy/);
  });

  it('wired + not-activatable + broker-unhealthy ⇒ LOUD error citing both reasons', async () => {
    const result = await runLeaseEnforcementDoctorCheck({
      readSettingsRaw: () => WIRED_SETTINGS_JSON,
      isActivatable: () => false,
      isBrokerHealthy: async () => false,
    });

    expect(result.status).toBe('error');
    expect(result.message).toMatch(/activation absent/);
    expect(result.message).toMatch(/broker not healthy/);
  });

  it('GREEN: wired + activatable + broker-healthy ⇒ ok', async () => {
    const result = await runLeaseEnforcementDoctorCheck({
      readSettingsRaw: () => WIRED_SETTINGS_JSON,
      isActivatable: () => true,
      isBrokerHealthy: async () => true,
    });

    expect(result.status).toBe('ok');
    expect(result.wired).toBe(true);
    expect(result.activatable).toBe(true);
    expect(result.brokerHealthy).toBe(true);
  });

  it('GREEN: not-wired ⇒ ok, no false alarm (activation/broker never probed)', async () => {
    let activatableCalled = false;
    let brokerCalled = false;

    const result = await runLeaseEnforcementDoctorCheck({
      readSettingsRaw: () => UNWIRED_SETTINGS_JSON,
      isActivatable: () => {
        activatableCalled = true;
        return false;
      },
      isBrokerHealthy: async () => {
        brokerCalled = true;
        return false;
      },
    });

    expect(result.status).toBe('ok');
    expect(result.wired).toBe(false);
    expect(result.activatable).toBeNull();
    expect(result.brokerHealthy).toBeNull();
    // Not wired must short-circuit — never even consult activation/broker.
    expect(activatableCalled).toBe(false);
    expect(brokerCalled).toBe(false);
  });

  it('GREEN: settings.json absent ⇒ ok (never touches a real file — readSettingsRaw is injected)', async () => {
    const result = await runLeaseEnforcementDoctorCheck({
      readSettingsRaw: () => null,
      isActivatable: () => false,
      isBrokerHealthy: async () => false,
    });

    expect(result.status).toBe('ok');
    expect(result.wired).toBe(false);
  });

  it('GREEN: malformed settings.json ⇒ ok (parse errors are not this card’s failure class)', async () => {
    const result = await runLeaseEnforcementDoctorCheck({
      readSettingsRaw: () => '{ not valid json',
      isActivatable: () => false,
      isBrokerHealthy: async () => false,
    });

    expect(result.status).toBe('ok');
  });
});
