import { describe, expect, it } from 'vitest';
import {
  EncounterResultSchema,
  MissionParticipantSchema,
  MissionSchema,
  PerkModifierSchema,
  PerkSchema,
  SurvivorSchema,
  SurvivorStatsSchema,
  SurvivorStateSchema,
} from '../index';

const PERK_ID = 'a0000000-0000-4000-8000-000000000001';
const SURVIVOR_ID = 'a0000000-0000-4000-8000-000000000002';
const MISSION_ID = 'a0000000-0000-4000-8000-000000000003';

const validPerk = {
  id: PERK_ID,
  key: 'borrowed_time',
  name: 'Borrowed Time',
  description: 'Extends the window for decisive action.',
  tags: ['rescue', 'altruism'],
  modifiers: [
    { target: 'altruism', type: 'additive', amount: 2 },
    {
      target: 'successChance',
      type: 'multiplicative',
      amount: 1.1,
      condition: { encounterTags: ['hook'] },
    },
  ],
};

const validSurvivor = {
  id: SURVIVOR_ID,
  opaqueUserId: 'U123456',
  channelId: 'ch_001',
  name: 'Ada',
  state: 'active',
  stats: { objectives: 7, survival: 5, altruism: 8 },
  perkSlots: [validPerk],
  createdAt: '2026-01-01T00:00:00.000Z',
};

const validMission = {
  id: MISSION_ID,
  groupId: null,
  participants: [
    {
      survivorId: SURVIVOR_ID,
      state: 'active',
      hookCount: 0,
    },
  ],
  difficulty: 2,
  status: 'active',
  encounterLibraryVersion: '1.0.0',
  nextTickAt: '2026-01-01T00:01:00.000Z',
  tickIndex: 0,
  startedAt: '2026-01-01T00:00:00.000Z',
  endedAt: null,
};

describe('SurvivorStateSchema', () => {
  it('accepts all valid states', () => {
    for (const s of ['idle', 'active', 'injured', 'downed', 'sacrificed']) {
      expect(SurvivorStateSchema.parse(s)).toBe(s);
    }
  });

  it('rejects unknown states', () => {
    expect(() => SurvivorStateSchema.parse('dead')).toThrow();
  });
});

describe('SurvivorStatsSchema', () => {
  it('rejects stats outside 1–10', () => {
    expect(() =>
      SurvivorStatsSchema.parse({ objectives: 0, survival: 5, altruism: 5 })
    ).toThrow();
    expect(() =>
      SurvivorStatsSchema.parse({ objectives: 5, survival: 11, altruism: 5 })
    ).toThrow();
  });
});

describe('PerkModifierSchema', () => {
  it('accepts a conditional modifier', () => {
    const result = PerkModifierSchema.parse(validPerk.modifiers[1]);
    expect(result.condition?.encounterTags).toEqual(['hook']);
  });
});

describe('PerkSchema', () => {
  it('parses a valid perk', () => {
    expect(PerkSchema.parse(validPerk).key).toBe('borrowed_time');
  });
});

describe('SurvivorSchema', () => {
  it('parses a valid survivor', () => {
    expect(SurvivorSchema.parse(validSurvivor).name).toBe('Ada');
  });

  it('rejects anonymous opaque user IDs', () => {
    expect(() =>
      SurvivorSchema.parse({ ...validSurvivor, opaqueUserId: 'A999' })
    ).toThrow();
  });

  it('rejects more than 4 perk slots', () => {
    expect(() =>
      SurvivorSchema.parse({
        ...validSurvivor,
        perkSlots: [validPerk, validPerk, validPerk, validPerk, validPerk],
      })
    ).toThrow();
  });
});

describe('MissionParticipantSchema', () => {
  it('rejects hookCount > 2', () => {
    expect(() =>
      MissionParticipantSchema.parse({
        survivorId: SURVIVOR_ID,
        state: 'downed',
        hookCount: 3,
      })
    ).toThrow();
  });
});

describe('MissionSchema', () => {
  it('parses a valid solo mission', () => {
    expect(MissionSchema.parse(validMission).difficulty).toBe(2);
  });

  it('rejects difficulty outside 1–3', () => {
    expect(() =>
      MissionSchema.parse({ ...validMission, difficulty: 4 })
    ).toThrow();
  });

  it('rejects groups larger than 4', () => {
    const participant = validMission.participants[0];
    expect(() =>
      MissionSchema.parse({
        ...validMission,
        participants: [
          participant,
          participant,
          participant,
          participant,
          participant,
        ],
      })
    ).toThrow();
  });
});

describe('EncounterResultSchema', () => {
  it('parses a successful encounter with no state change', () => {
    const result = EncounterResultSchema.parse({
      missionId: MISSION_ID,
      survivorId: SURVIVOR_ID,
      encounterKey: 'cleanse_hex',
      tickIndex: 1,
      seed: 'abc123',
      success: true,
      survivorStateChange: null,
      modifiersApplied: [],
      logText: 'Ada cleanses the hex totem. The fog thins briefly.',
    });
    expect(result.success).toBe(true);
    expect(result.survivorStateChange).toBeNull();
  });

  it('parses an encounter that injures the survivor', () => {
    const result = EncounterResultSchema.parse({
      missionId: MISSION_ID,
      survivorId: SURVIVOR_ID,
      encounterKey: 'stalked',
      tickIndex: 2,
      seed: 'def456',
      success: false,
      survivorStateChange: { from: 'active', to: 'injured' },
      modifiersApplied: [
        {
          perkKey: 'borrowed_time',
          target: 'survival',
          type: 'additive',
          effectiveAmount: 2,
        },
      ],
      logText: 'Something moves in the trees. Ada takes a hit.',
    });
    expect(result.survivorStateChange?.to).toBe('injured');
  });
});
