namespace ReactorMaintenance.Simulation;

internal static class ReactorSystem
{
    public static LevelState Activate(LevelState level)
    {
        var reactorIndex = level.Reactors.ToList().FindIndex(reactor => reactor.ControlPosition == level.Robot.Position);
        if (reactorIndex < 0)
            return Refuse(level, "NO REACTOR CONTROL");

        var reactor = level.Reactors[reactorIndex];
        if (!reactor.Ready)
            return Refuse(level, "REACTOR NOT READY");

        var reactors = level.Reactors.ToArray();
        reactors[reactorIndex] = reactor with { Activated = true };
        return level with {
            Reactors = reactors,
            Global = level.Global with { LevelState = ELevelState.Won, Status = "REACTOR ONLINE" }
        };
    }

    public static LevelState DeriveState(LevelState level)
    {
        if (level.Global.LevelState is ELevelState.Lost or ELevelState.Won)
            return level;

        var reactors = level.Reactors.Select(reactor => reactor with { Ready = IsReady(level, reactor) }).ToArray();
        if (reactors.Any(reactor => reactor.Ready))
            return level with { Reactors = reactors, Global = level.Global with { LevelState = ELevelState.Ready, Status = "REACTOR READY" } };

        var maxHeat = level.Surface.Where((_, index) => level.Terrain[index] == ECellTerrain.Floor).Select(surface => surface.Heat).DefaultIfEmpty(0).Max();
        if (maxHeat >= Balancing.Current.TerminalHeat)
            return level with { Reactors = reactors, Global = level.Global with { LevelState = ELevelState.Lost, TerminalLoss = true, Status = "REACTOR HEAT TERMINAL" } };

        var hasCritical = level.Surface.Any(surface => SimulationBands.Fuel(surface.Fuel) == EBand.Critical || SimulationBands.Coolant(surface.Coolant) == EBand.Critical || SimulationBands.Electricity(surface.Electricity) == EBand.Critical || SimulationBands.Heat(surface.Heat) == EBand.Critical);
        var hasCaution = hasCritical
                      || !HasRequiredConsumers(level)
                      || level.Props.Any(prop => prop.Type == EPropType.Consumer && HasConsumerTrouble(prop))
                      || level.Leaks.Any(leak => !leak.Repaired);
        var state = hasCritical ? ELevelState.Critical :
            hasCaution ? ELevelState.Caution : ELevelState.Stable;
        return level with { Reactors = reactors, Global = level.Global with { LevelState = state, Status = state.ToString().ToUpperInvariant() } };
    }

    private static bool IsReady(LevelState level, ReactorState reactor)
    {
        return ReactorFeedsPresentAndProducing(level, reactor.ControlPosition)
            && ProducingConsumerCount(level, ECarrierType.Fuel) >= level.RequiredFuelConsumers
            && ProducingConsumerCount(level, ECarrierType.Coolant) >= level.RequiredCoolantConsumers
            && ProducingConsumerCount(level, ECarrierType.Electricity) >= level.RequiredElectricityConsumers
            && level.GetSurface(reactor.ControlPosition).Heat < Balancing.Current.TerminalHeat;
    }

    private static bool ReactorFeedsPresentAndProducing(LevelState level, GridPosition position)
    {
        foreach (var carrier in Enum.GetValues<ECarrierType>())
        {
            var underground = level.GetUnderground(position, carrier);
            if (underground.IsPresent && (underground.Amount <= 0 || underground.Intensity <= 0))
                return false;
        }

        return true;
    }

    private static int ProducingConsumerCount(LevelState level, ECarrierType carrier)
    {
        return LevelTraversal.AllPositions(level)
                             .Count(position => level.GetProp(position) is { Type: EPropType.Consumer } prop && prop.ServiceStateFor(carrier) == EConsumerServiceState.Producing);
    }

    private static bool HasRequiredConsumers(LevelState level)
    {
        return ProducingConsumerCount(level, ECarrierType.Fuel) >= level.RequiredFuelConsumers
            && ProducingConsumerCount(level, ECarrierType.Coolant) >= level.RequiredCoolantConsumers
            && ProducingConsumerCount(level, ECarrierType.Electricity) >= level.RequiredElectricityConsumers;
    }

    private static bool HasConsumerTrouble(PropState prop)
    {
        return prop.FuelServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled
            || prop.CoolantServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled
            || prop.ElectricityServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled;
    }

    private static LevelState Refuse(LevelState level, string message)
    {
        return level with { Global = level.Global with { Status = message } };
    }
}