# Copyright 2026 Espressif Systems (Shanghai) Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import queue
import sys
import unittest

# Add parent path
# Add parent path
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
sys.path.append(project_root)

from esp_viewer.core.backend import DeviceService
from esp_viewer.core.protocol import CommandType, EventType, LogLevel, Packet


class TestBackendLogic(unittest.TestCase):
    def setUp(self):
        self.profile = {
            "parameters": [{"id": "p1", "set_template": "SET {value}"}],
            "streams": [
                {"regex": r"^DATA,(\d+),([\d\.]+)$", "fields": ["ts", "val"]},
                {"type": "log", "regex": r"^LOG:(.*)$", "fields": ["message"]},
            ],
        }
        self.cmd_q = queue.Queue()
        self.evt_q = queue.Queue()
        self.backend = DeviceService(self.cmd_q, self.evt_q, self.profile)

        # Mock device interface
        class MockInterface:
            def __init__(self):
                self.writes = []

            def write(self, data):
                self.writes.append(data)

            def readline(self):
                return None

            def open(self):
                pass

            def close(self):
                pass

        self.mock_iface = MockInterface()
        self.backend.set_device_interface(self.mock_iface)
        # Drain initial packets (like IO_LOG_STATUS)
        while not self.evt_q.empty():
            self.evt_q.get()
        self.backend.connected = True  # Force connected

    def test_parse_data(self):
        line = "DATA,100,5.5"
        self.backend.stream_enabled = True
        self.backend._parse_line(line)

        pkt = self.evt_q.get()
        self.assertEqual(pkt.type, EventType.STREAM_DATA)
        self.assertEqual(pkt.payload["values"]["ts"], 100)
        self.assertEqual(pkt.payload["values"]["val"], 5.5)

    def test_parse_log(self):
        line = "LOG:System Ready"
        self.backend._parse_line(line)

        pkt = self.evt_q.get()
        self.assertEqual(pkt.type, EventType.LOG_MSG)
        self.assertEqual(pkt.payload["level"], LogLevel.INFO.value)
        self.assertEqual(pkt.payload["msg"], "[INFO] System Ready")

    def test_command_generation_template(self):
        # p1 has set_template: "SET {value}"
        self.backend._set_param({"id": "p1", "value": 99})
        last_write = self.mock_iface.writes[-1].strip()
        self.assertEqual(last_write, "SET 99")

    def test_command_generation_json_rpc(self):
        # p2 has NO set_template, should fallback to JSON-RPC
        self.backend.param_map["p2"] = {"id": "p2"}
        self.backend._set_param({"id": "p2", "value": 123})

        import json

        last_write = self.mock_iface.writes[-1].strip()
        cmd = json.loads(last_write)
        self.assertEqual(cmd["method"], "set_config")
        self.assertEqual(cmd["params"]["p2"], 123)


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