/**
 * Favors & crisis support — affinity-as-gameplay-gate (T008).
 *
 * Verifies the core payoff: the SUCCESS and MAGNITUDE of a favor scale with the
 * target NPC's affinity, and crisis support only fires above a threshold.
 */
import { describe, it, expect } from 'vitest';
import {
  evaluateFavor,
  evaluateCrisisSupport,
  CRISIS_SUPPORT_THRESHOLD,
  FAVOR_DECLINE_THRESHOLD,
  type FavorTarget,
} from '../../../src/services/relationships/favors.js';

function npc(affinity: number, extra: Partial<FavorTarget> = {}): FavorTarget {
  return { id: 'npc-1', firstname: 'Sam', affinity, ...extra };
}

describe('evaluateFavor — loans scale with affinity', () => {
  it('grants a LARGER loan to a high-affinity NPC than a low-affinity one', () => {
    const high = evaluateFavor(npc(80), 'loan');
    const low = evaluateFavor(npc(30), 'loan');

    expect(high.granted).toBe(true);
    expect(low.granted).toBe(true);
    expect(high.moneyGranted).toBeGreaterThan(low.moneyGranted);
    // affinity 80 -> ~$800, affinity 30 -> ~$300 (before familiarity bump)
    expect(high.moneyGranted).toBeGreaterThanOrEqual(800);
    expect(low.moneyGranted).toBeGreaterThanOrEqual(300);
    expect(low.moneyGranted).toBeLessThan(500);
  });

  it('caps the loan at the requested amount', () => {
    const result = evaluateFavor(npc(90), 'loan', 100);
    expect(result.granted).toBe(true);
    expect(result.moneyGranted).toBe(100);
  });

  it('DECLINES any favor when affinity is at or below the decline threshold', () => {
    const result = evaluateFavor(npc(FAVOR_DECLINE_THRESHOLD), 'loan');
    expect(result.granted).toBe(false);
    expect(result.moneyGranted).toBe(0);
  });

  it('declines a loan from a mildly-positive but not-close NPC (affinity < 20)', () => {
    const result = evaluateFavor(npc(10), 'loan');
    expect(result.granted).toBe(false);
    expect(result.moneyGranted).toBe(0);
  });

  it('gives a small familiarity bump to the loan magnitude', () => {
    const noHistory = evaluateFavor(npc(80, { familiarity: 0 }), 'loan');
    const deepHistory = evaluateFavor(npc(80, { familiarity: 100 }), 'loan');
    expect(deepHistory.moneyGranted).toBeGreaterThan(noHistory.moneyGranted);
  });
});

describe('evaluateFavor — job referrals scale with affinity', () => {
  it('gives a stronger referral the higher the affinity', () => {
    const beloved = evaluateFavor(npc(100), 'job_referral');
    const lukewarm = evaluateFavor(npc(40), 'job_referral');

    expect(beloved.granted).toBe(true);
    expect(lukewarm.granted).toBe(true);
    expect(beloved.referralStrength).toBeGreaterThan(lukewarm.referralStrength);
    expect(beloved.referralStrength).toBeCloseTo(1, 5);
    expect(lukewarm.referralStrength).toBeCloseTo(0.25, 5);
  });

  it('refuses to vouch when affinity is at the floor (referralStrength 0)', () => {
    const result = evaluateFavor(npc(20), 'job_referral');
    expect(result.referralStrength).toBe(0);
    expect(result.granted).toBe(false);
  });
});

describe('evaluateFavor — practical help / childcare are affinity-gated', () => {
  it('helps above affinity 25, declines below', () => {
    expect(evaluateFavor(npc(50), 'practical_help').granted).toBe(true);
    expect(evaluateFavor(npc(50), 'childcare').granted).toBe(true);
    expect(evaluateFavor(npc(15), 'practical_help').granted).toBe(false);
  });
});

describe('evaluateCrisisSupport — only high-affinity relations rally', () => {
  it('fires when a relation is at/above the crisis threshold', () => {
    const result = evaluateCrisisSupport([
      npc(20),
      npc(CRISIS_SUPPORT_THRESHOLD + 10),
    ]);
    expect(result.supported).toBe(true);
    expect(result.moneyProvided).toBeGreaterThan(0);
    expect(result.moodBuffer).toBeGreaterThan(0);
    expect(result.supporter?.affinity).toBe(CRISIS_SUPPORT_THRESHOLD + 10);
  });

  it('does NOT fire when every relation is below the threshold', () => {
    const result = evaluateCrisisSupport([npc(10), npc(40), npc(CRISIS_SUPPORT_THRESHOLD - 1)]);
    expect(result.supported).toBe(false);
    expect(result.moneyProvided).toBe(0);
    expect(result.supporter).toBeNull();
  });

  it('picks the highest-affinity supporter and scales help with severity', () => {
    const relations = [npc(70), npc(95)];
    const minor = evaluateCrisisSupport(relations, 1);
    const major = evaluateCrisisSupport(relations, 3);
    expect(minor.supporter?.affinity).toBe(95);
    expect(major.moneyProvided).toBeGreaterThan(minor.moneyProvided);
  });

  it('ignores deceased relations even if they had high affinity', () => {
    const result = evaluateCrisisSupport([
      { id: 'd', firstname: 'Gone', affinity: 100, status: 'dead' },
    ]);
    expect(result.supported).toBe(false);
  });
});
