using ReactorMaintenance.Simulation.Difficulties;

namespace ReactorMaintenance.Simulation;

public abstract class Balancing
{
    public float ClampValue(float value)
    {
        return Math.Clamp(value, MinValue, MaxValue);
    }

    public EBand Band(float value, float caution, float critical)
    {
        if (value >= critical)
            return EBand.Critical;

        return value >= caution ? EBand.Caution : EBand.Safe;
    }

    public IReadOnlyList<JunctionRatioPreset> JunctionRatios(int outflowCount)
    {
        return outflowCount switch {
            2 => TwoOutflowJunctionRatios,
            3 => ThreeOutflowJunctionRatios,
            _ => Array.Empty<JunctionRatioPreset>()
        };
    }

    public float[] JunctionWeights(int outflowCount, int mode)
    {
        var ratios = JunctionRatios(outflowCount);
        if (ratios.Count == 0)
            return Array.Empty<float>();

        return ratios[Math.Clamp(mode, 0, ratios.Count - 1)].Weights;
    }

    public SurfaceInteractionEffect SameCellInteraction(ECarrierType? rowCarrier, EBand rowBand, ECarrierType? colCarrier, EBand colBand)
    {
        if (rowBand == EBand.Safe && colBand == EBand.Safe)
            return SurfaceInteractionEffect.Hold;

        if (rowCarrier == ECarrierType.Fuel && colCarrier == ECarrierType.Electricity)
            return Ignite(rowBand, colBand);

        if (rowCarrier == ECarrierType.Fuel && colCarrier is null)
            return rowBand == EBand.Critical || colBand == EBand.Critical ? Ignite(rowBand, colBand) : Warm(rowBand, colBand);

        if (rowCarrier == ECarrierType.Coolant && colCarrier == ECarrierType.Electricity)
            return Short(rowBand, colBand);

        if (rowCarrier == ECarrierType.Coolant && colCarrier is null)
            return Quench(rowBand, colBand);

        return SurfaceInteractionEffect.Hold;
    }

    public SurfaceInteractionEffect FlowInteraction(ESurfaceQuantity quantity)
    {
        return new() { Verb = ESurfaceInteractionVerb.Flow, Quantity = quantity, Amount = FlowTransferRatio };
    }

    private SurfaceInteractionEffect Warm(EBand rowBand, EBand colBand)
    {
        return new() {
            Verb = ESurfaceInteractionVerb.Warm,
            Quantity = ESurfaceQuantity.Heat,
            Amount = Strongest(rowBand, colBand) == EBand.Critical ? WarmCriticalAmount : WarmCautionAmount
        };
    }

    private SurfaceInteractionEffect Quench(EBand rowBand, EBand colBand)
    {
        return new() {
            Verb = ESurfaceInteractionVerb.Quench,
            Quantity = ESurfaceQuantity.Heat,
            Amount = Strongest(rowBand, colBand) == EBand.Critical ? QuenchCriticalAmount : QuenchCautionAmount
        };
    }

    private SurfaceInteractionEffect Short(EBand rowBand, EBand colBand)
    {
        var critical = Strongest(rowBand, colBand) == EBand.Critical;
        return new() {
            Verb = ESurfaceInteractionVerb.Short,
            Quantity = ESurfaceQuantity.Electricity,
            Amount = critical ? ShortCriticalHeat : ShortCautionHeat,
            SecondaryAmount = critical ? ShortCriticalDischarge : ShortCautionDischarge
        };
    }

    private SurfaceInteractionEffect Ignite(EBand rowBand, EBand colBand)
    {
        var critical = Strongest(rowBand, colBand) == EBand.Critical;
        return new() {
            Verb = ESurfaceInteractionVerb.Ignite,
            Quantity = ESurfaceQuantity.Fuel,
            Amount = critical ? IgniteCriticalHeat : IgniteCautionHeat,
            SecondaryAmount = critical ? IgniteCriticalFuelConsumption : IgniteCautionFuelConsumption
        };
    }

    private static EBand Strongest(EBand a, EBand b)
    {
        return a > b ? a : b;
    }

    public static Balancing Current { get; set; } = new NormalBalancing();

    public abstract int DefaultLevelWidth { get; }
    public abstract int DefaultLevelHeight { get; }
    public abstract int MinimumLevelSize { get; }
    public abstract int ActionsPerTurn { get; }
    public abstract int ForecastHorizon { get; }
    public abstract float MinValue { get; }
    public abstract float MaxValue { get; }
    public abstract float FuelSafe { get; }
    public abstract float FuelCaution { get; }
    public abstract float FuelCritical { get; }
    public abstract float CoolantSafe { get; }
    public abstract float CoolantCaution { get; }
    public abstract float CoolantCritical { get; }
    public abstract float ElectricitySafe { get; }
    public abstract float ElectricityCaution { get; }
    public abstract float ElectricityCritical { get; }
    public abstract float HeatSafe { get; }
    public abstract float HeatCaution { get; }
    public abstract float HeatCritical { get; }
    public abstract float TerminalHeat { get; }
    public abstract float RobotFuelSafetyThreshold { get; }
    public abstract float RobotCoolantSafetyThreshold { get; }
    public abstract float RobotElectricitySafetyThreshold { get; }
    public abstract float RobotHeatSafetyThreshold { get; }
    public abstract float SourceAmount { get; }
    public abstract float SourceIntensity { get; }
    public abstract float DistanceAmountFalloff { get; }
    public abstract float DistanceIntensityFalloff { get; }
    public abstract IReadOnlyList<JunctionRatioPreset> TwoOutflowJunctionRatios { get; }
    public abstract IReadOnlyList<JunctionRatioPreset> ThreeOutflowJunctionRatios { get; }
    public abstract float ConsumerRequiredAmount { get; }
    public abstract float ConsumerRequiredIntensity { get; }
    public abstract float LeakBaseAmount { get; }
    public abstract float LeakAmountScale { get; }
    public abstract float LeakIntensityScale { get; }
    public abstract float FlowTransferRatio { get; }
    public abstract float WarmCautionAmount { get; }
    public abstract float WarmCriticalAmount { get; }
    public abstract float QuenchCautionAmount { get; }
    public abstract float QuenchCriticalAmount { get; }
    public abstract float ShortCautionHeat { get; }
    public abstract float ShortCautionDischarge { get; }
    public abstract float ShortCriticalHeat { get; }
    public abstract float ShortCriticalDischarge { get; }
    public abstract float IgniteCautionHeat { get; }
    public abstract float IgniteCautionFuelConsumption { get; }
    public abstract float IgniteCriticalHeat { get; }
    public abstract float IgniteCriticalFuelConsumption { get; }
    public abstract int RemedyBlockTurns { get; }
    public abstract int HeatShieldSteps { get; }
    public abstract int InventoryCapacityPerRemedy { get; }
}