namespace ReactorMaintenance.Simulation;

public sealed class SimulationEngine
{
    public LevelState MoveRobot(LevelState level, GridPosition destination)
    {
        return PlayerActionSystem.MoveRobot(level, destination, SpendAction);
    }

    public LevelState InteractProp(LevelState level)
    {
        return PlayerActionSystem.InteractProp(level, SpendAction);
    }

    public LevelState InteractLeak(LevelState level, ECarrierType carrier, bool useRemedy)
    {
        return PlayerActionSystem.InteractLeak(level, carrier, useRemedy, SpendAction);
    }

    public LevelState ApplyHeatShield(LevelState level)
    {
        return PlayerActionSystem.ApplyHeatShield(level, SpendAction);
    }

    public LevelState ActivateReactor(LevelState level)
    {
        return ReactorSystem.Activate(level);
    }

    public LevelState EndTurn(LevelState level)
    {
        return ResolveTurn(level with { Global = level.Global with { ActionsRemaining = 0 } });
    }

    public LevelState AdvanceTurn(LevelState level)
    {
        return ResolveTurn(level);
    }

    public IReadOnlyList<Forecast> Forecast(LevelState level)
    {
        return ForecastSystem.Forecast(level, simulated => ResolveTurn(simulated, false));
    }

    private LevelState SpendAction(LevelState level)
    {
        var actions = Math.Max(0, level.Global.ActionsRemaining - 1);
        var next = level with { Global = level.Global with { ActionsRemaining = actions } };
        return actions == 0 ? ResolveTurn(next) : next;
    }

    private LevelState ResolveTurn(LevelState level, bool refreshForecasts = true)
    {
        var report = m_Validator.Validate(level);
        if (!report.IsValid)
            return level with { Global = level.Global with { LevelState = ELevelState.Lost, Status = report.Errors[0].Message } };

        var next = RuleEventSystem.Apply(level, ERuleEventPhase.StartOfSimulation);
        next = NetworkPropagationSystem.Propagate(next);
        next = ConsumerSystem.Resolve(next);
        next = LeakSystem.Inject(next);
        next = SurfaceInteractionSystem.Resolve(next);
        next = RobotSafetySystem.Resolve(next);
        next = ReactorSystem.DeriveState(next);
        next = RuleEventSystem.Apply(next, ERuleEventPhase.EndOfTurn);
        next = SurfaceInteractionSystem.AdvanceDurations(next);
        next = next with {
            Global = next.Global with {
                Turn = next.Global.Turn + 1,
                ActionsRemaining = Balancing.Current.ActionsPerTurn
            }
        };

        return refreshForecasts ? next with { Forecasts = Forecast(next) } : next;
    }

    private readonly LevelValidator m_Validator = new();
}