from neuron import Neuron
from synapse import Synapse


class TrackingTargetNeuron:
    def __init__(self, should_fire: bool):
        self.id = "tracking-target"
        self.should_fire = bool(should_fire)
        self.input_values = []
        self.output_values = []

    def on_input(self, value):
        self.input_values.append(float(value))
        return self.should_fire

    def on_output(self, amplitude):
        self.output_values.append(float(amplitude))


class RelayNeuron(Neuron):
    def __init__(self):
        super().__init__("relay-target")
        self.input_values = []
        self.output_values = []

    def on_input(self, value):
        self.input_values.append(float(value))
        self.fire(float(value))
        return True

    def on_output(self, amplitude):
        self.output_values.append(float(amplitude))


def test_synapse_deliver_returns_target_fire_state() -> None:
    source_neuron = Neuron("source-neuron")
    firing_target = TrackingTargetNeuron(should_fire=True)
    synapse = Synapse(source_neuron, firing_target)

    fired = synapse.deliver(0.75)

    assert fired is True
    assert firing_target.input_values == [0.75]
    assert firing_target.output_values == []


def test_neuron_fire_runs_follow_up_once_per_synapse() -> None:
    source_neuron = Neuron("source-neuron")
    firing_target = TrackingTargetNeuron(should_fire=True)
    source_neuron.connect(firing_target, feedback=False)

    source_neuron.fire(0.5)

    assert firing_target.input_values == [0.5]
    assert firing_target.output_values == [0.5]


def test_neuron_fire_preserves_downstream_propagation_chain() -> None:
    source_neuron = Neuron("source-neuron")
    relay_neuron = RelayNeuron()
    sink_target = TrackingTargetNeuron(should_fire=False)
    source_neuron.connect(relay_neuron, feedback=False)
    relay_neuron.connect(sink_target, feedback=False)

    source_neuron.fire(0.9)

    assert relay_neuron.input_values == [0.9]
    assert relay_neuron.output_values == [0.9]
    assert sink_target.input_values == [0.9]
