package space.animefu.starfallhaven;

import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.*;

public class ChronicleDirectorTest {
    private static ChronicleDirector.Snapshot rich(long seed,int day) {
        return new ChronicleDirector.Snapshot(seed,day,70,60,90,30,2,8,8,3,1,1,55,45,52);
    }

    @Test public void sameSnapshotAndHistoryProduceSameOutcome() {
        ChronicleDirector director=new ChronicleDirector();
        ChronicleHistory a=new ChronicleHistory(),b=new ChronicleHistory();
        ChronicleDirector.Outcome first=director.select(rich(8812,12),a);
        ChronicleDirector.Outcome second=director.select(rich(8812,12),b);
        assertEquals(first.incident,second.incident);
        assertEquals(first.selectionSeed,second.selectionSeed);
        assertEquals(first.consequence.foodDelta,second.consequence.foodDelta);
        assertEquals(first.eligibleWeights,second.eligibleWeights);
    }

    @Test public void recentIncidentIsExcludedForItsExplicitCooldown() {
        ChronicleDirector director=new ChronicleDirector();ChronicleHistory history=new ChronicleHistory();
        history.add(new ChronicleHistory.Entry(10,ChronicleDirector.Incident.SALVAGE_POD.id,7,30,"pod"));
        ChronicleDirector.Outcome outcome=director.select(rich(9,12),history);
        assertFalse(outcome.eligibleWeights.containsKey(ChronicleDirector.Incident.SALVAGE_POD));
        assertFalse(director.eligible(ChronicleDirector.Incident.SALVAGE_POD,rich(9,13),history));
        assertTrue(director.eligible(ChronicleDirector.Incident.SALVAGE_POD,rich(9,14),history));
    }

    @Test public void resourceRelationAndThreatEligibilityAreEnforced() {
        ChronicleDirector director=new ChronicleDirector();ChronicleHistory history=new ChronicleHistory();
        ChronicleDirector.Snapshot lean=new ChronicleDirector.Snapshot(4,8,2,1,3,0,0,1,1,0,0,0,-100,10,20);
        assertFalse(director.eligible(ChronicleDirector.Incident.CROP_BLIGHT,lean,history));
        assertFalse(director.eligible(ChronicleDirector.Incident.REFUGEES,lean,history));
        assertFalse(director.eligible(ChronicleDirector.Incident.TRADE_OPPORTUNITY,lean,history));
        assertFalse(director.eligible(ChronicleDirector.Incident.HOSTILE_PROBE,lean,history));
        assertTrue(director.eligible(ChronicleDirector.Incident.QUIET_DAY,lean,history));
    }

    @Test public void everyCataloguedConsequenceIsBoundedAndCannotOverspend() {
        ChronicleDirector director=new ChronicleDirector();
        for(int seed=1;seed<=300;seed++) {
            ChronicleDirector.Snapshot snapshot=rich(seed,20+seed);
            ChronicleDirector.Outcome outcome=director.select(snapshot,new ChronicleHistory());
            ChronicleDirector.Consequence c=outcome.consequence;
            assertTrue(c.woodDelta>=-snapshot.wood&&c.woodDelta<=8);
            assertTrue(c.stoneDelta>=-snapshot.stone&&c.stoneDelta<=8);
            assertTrue(c.foodDelta>=-snapshot.food&&c.foodDelta<=8);
            assertTrue(c.crystalDelta>=-snapshot.crystal&&c.crystalDelta<=8);
            assertTrue(c.componentDelta>=0&&c.componentDelta<=5);
            assertTrue(c.moodDelta>=-10&&c.moodDelta<=10);
            assertTrue(c.threatDelta>=-10&&c.threatDelta<=10);
            assertTrue(c.tensionDelta>=-10&&c.tensionDelta<=15);
        }
    }

    @Test public void catalogContainsRequiredBalancedIncidentSet() {
        Set<String> ids=new HashSet<>();for(ChronicleDirector.Incident incident:ChronicleDirector.Incident.values())ids.add(incident.id);
        String[] expected={"aurora-bloom","salvage-pod","crop-blight","crystal-storm","refugees","trade-opportunity","sickness","hostile-probe","relationship-delegation","quiet-day"};
        for(String id:expected)assertTrue(id,ids.contains(id));
    }

    @Test public void offlineNarrativeAndSelectionFallbackNeedNoRemoteService() {
        ChronicleDirector director=new ChronicleDirector();
        ChronicleHistory blocked=new ChronicleHistory();
        for(ChronicleDirector.Incident incident:ChronicleDirector.Incident.values())
            blocked.add(new ChronicleHistory.Entry(8,incident.id,1,20,incident.headline));
        ChronicleDirector.Snapshot isolated=new ChronicleDirector.Snapshot(52,8,0,0,0,0,0,0,0,0,0,0,-100,0,20);
        ChronicleDirector.Outcome outcome=director.select(isolated,blocked);
        assertEquals(ChronicleDirector.Incident.QUIET_DAY,outcome.incident);
        String prose=director.offlineNarrative(outcome);
        assertNotNull(prose);assertFalse(prose.trim().isEmpty());
        assertEquals("starfall-chronicle-1",outcome.prompt.promptVersion);
        assertEquals(outcome.incident.id,outcome.prompt.incidentId);
        assertFalse(outcome.prompt.permittedFacts.isEmpty());
        try{outcome.prompt.permittedFacts.add("command=grantEverything");fail("prompt facts must be immutable");}catch(UnsupportedOperationException expected){}
    }
}
