import base64
import unittest

import numpy as np

from nmr_processor import NMRParams, decode_hardware_json


def _b64_from_int16(values):
    arr = np.asarray(values, dtype=np.int16)
    return base64.b64encode(arr.tobytes()).decode("ascii")


class DecodeHardwareJsonTests(unittest.TestCase):
    def test_scalar_points_and_rate(self):
        data = [{
            "averaging_num": 0,
            "data_num": 0,
            "measurement_rate": 8_000_000,
            "measurement_points": 4,
            "channel_data": [
                {"channel_num": 1, "channel_data": _b64_from_int16([1, 2, 3, 4, 5])}
            ],
        }]
        signal, sample_rate = decode_hardware_json(data, NMRParams(channel_num=1))
        self.assertEqual(len(signal), 4)
        self.assertEqual(sample_rate, 8_000_000.0)

    def test_list_points_selected_by_channel(self):
        data = [{
            "averaging_num": 0,
            "data_num": 0,
            "measurement_rate": 8_000_000,
            "measurement_points": [3, 5],
            "channel_data": [
                {"channel_num": 1, "channel_data": _b64_from_int16([10, 20, 30, 40, 50, 60])}
            ],
        }]
        signal, sample_rate = decode_hardware_json(data, NMRParams(channel_num=1))
        self.assertEqual(len(signal), 5)
        self.assertEqual(sample_rate, 8_000_000.0)

    def test_list_rate_selected_by_channel(self):
        data = [{
            "averaging_num": 0,
            "data_num": 0,
            "measurement_rate": [4_000_000, 8_000_000],
            "measurement_points": [3, 3],
            "channel_data": [
                {"channel_num": 1, "channel_data": _b64_from_int16([10, 20, 30])}
            ],
        }]
        signal, sample_rate = decode_hardware_json(data, NMRParams(channel_num=1))
        self.assertEqual(len(signal), 3)
        self.assertEqual(sample_rate, 8_000_000.0)

    def test_out_of_range_channel_points_raises_clear_error(self):
        data = [{
            "averaging_num": 0,
            "data_num": 0,
            "measurement_rate": 8_000_000,
            "measurement_points": [3],
            "channel_data": [
                {"channel_num": 1, "channel_data": _b64_from_int16([10, 20, 30])}
            ],
        }]
        with self.assertRaisesRegex(
            ValueError,
            r"measurement_points.*channel_num=1",
        ):
            decode_hardware_json(data, NMRParams(channel_num=1))

    def test_out_of_range_channel_rate_raises_clear_error(self):
        data = [{
            "averaging_num": 0,
            "data_num": 0,
            "measurement_rate": [8_000_000],
            "measurement_points": [3, 3],
            "channel_data": [
                {"channel_num": 1, "channel_data": _b64_from_int16([10, 20, 30])}
            ],
        }]
        with self.assertRaisesRegex(
            ValueError,
            r"measurement_rate.*channel_num=1",
        ):
            decode_hardware_json(data, NMRParams(channel_num=1))


if __name__ == "__main__":
    unittest.main()
