"""Tests for J-Link flash CLI integration."""

from __future__ import annotations

import os
import tempfile
from unittest.mock import MagicMock, patch

import pytest

from eab.cli.flash import cmd_flash


@pytest.fixture
def hex_firmware():
    """Create a temporary .hex firmware file."""
    with tempfile.NamedTemporaryFile(suffix=".hex", delete=False) as f:
        f.write(b":00000001FF\n")
        hex_path = f.name
    
    yield hex_path
    
    # Cleanup
    if os.path.exists(hex_path):
        os.unlink(hex_path)


@pytest.fixture
def bin_firmware():
    """Create a temporary .bin firmware file."""
    with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f:
        f.write(b"\x00\x01\x02\x03")
        bin_path = f.name
    
    yield bin_path
    
    # Cleanup
    if os.path.exists(bin_path):
        os.unlink(bin_path)


def test_cmd_flash_jlink_hex_success(hex_firmware):
    """Test cmd_flash with --tool jlink successfully flashes .hex file."""
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        # Mock successful flash
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = "J-Link flash successful"
        mock_result.stderr = ""
        mock_run.return_value = mock_result
        
        exit_code = cmd_flash(
            firmware=hex_firmware,
            chip="nrf5340",
            address=None,
            port=None,
            tool="jlink",
            baud=115200,
            connect_under_reset=False,
            board=None,
            runner=None,
            device="NRF5340_XXAA_APP",
            reset_after=True,
            json_mode=True,
        )
        
        # Should succeed
        assert exit_code == 0
        
        # Verify subprocess was called with JLinkExe
        assert mock_run.called
        call_args = mock_run.call_args[0][0]
        assert call_args[0] == "JLinkExe"
        assert "-CommanderScript" in call_args
        
        # Verify temp script was created and cleaned up
        call_args[2]
        # Script should be cleaned up after execution
        # (in real execution; mocked here)


def test_cmd_flash_jlink_bin_success(bin_firmware):
    """Test cmd_flash with --tool jlink successfully flashes .bin file."""
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = "J-Link flash successful"
        mock_result.stderr = ""
        mock_run.return_value = mock_result
        
        exit_code = cmd_flash(
            firmware=bin_firmware,
            chip="nrf5340",
            address=None,
            port=None,
            tool="jlink",
            baud=115200,
            connect_under_reset=False,
            device="NRF5340_XXAA_APP",
            reset_after=True,
            json_mode=True,
        )
        
        assert exit_code == 0
        assert mock_run.called


def test_cmd_flash_jlink_net_core_no_reset(hex_firmware):
    """Test cmd_flash with --tool jlink and NET core (no reset)."""
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = "J-Link flash successful"
        mock_result.stderr = ""
        mock_run.return_value = mock_result
        
        exit_code = cmd_flash(
            firmware=hex_firmware,
            chip="nrf5340",
            address=None,
            port=None,
            tool="jlink",
            baud=115200,
            connect_under_reset=False,
            device="NRF5340_XXAA_NET",
            reset_after=False,
            json_mode=True,
        )
        
        assert exit_code == 0


def test_cmd_flash_jlink_non_zephyr_chip_error():
    """Test cmd_flash with --tool jlink fails for non-Zephyr chips."""
    with tempfile.NamedTemporaryFile(suffix=".hex", delete=False) as f:
        hex_path = f.name
        f.write(b":00000001FF\n")
    
    try:
        exit_code = cmd_flash(
            firmware=hex_path,
            chip="esp32s3",  # Not a Zephyr chip
            address=None,
            port=None,
            tool="jlink",
            baud=115200,
            connect_under_reset=False,
            device="NRF5340_XXAA_APP",
            reset_after=True,
            json_mode=True,
        )
        
        # Should fail with error code 2
        assert exit_code == 2
    finally:
        os.unlink(hex_path)


def test_cmd_flash_jlink_missing_file_error():
    """Test cmd_flash with --tool jlink fails for missing firmware file."""
    exit_code = cmd_flash(
        firmware="/nonexistent/file.hex",
        chip="nrf5340",
        address=None,
        port=None,
        tool="jlink",
        baud=115200,
        connect_under_reset=False,
        device="NRF5340_XXAA_APP",
        reset_after=True,
        json_mode=True,
    )
    
    # Should fail with error code 1
    assert exit_code == 1


def test_cmd_flash_jlink_default_device(hex_firmware):
    """Test cmd_flash with --tool jlink uses default device when not specified."""
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = "Success"
        mock_result.stderr = ""
        mock_run.return_value = mock_result
        
        exit_code = cmd_flash(
            firmware=hex_firmware,
            chip="nrf5340",
            address=None,
            port=None,
            tool="jlink",
            baud=115200,
            connect_under_reset=False,
            device=None,  # Not specified
            reset_after=True,
            json_mode=True,
        )
        
        # Should succeed with default device
        assert exit_code == 0


def test_cmd_flash_jlink_script_cleanup(hex_firmware):
    """Test that J-Link script file is cleaned up after flash."""
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = "Success"
        mock_result.stderr = ""
        mock_run.return_value = mock_result
        
        # Track created script files
        created_scripts = []
        
        # Patch tempfile.mkstemp to track created files
        original_mkstemp = tempfile.mkstemp
        
        def tracking_mkstemp(*args, **kwargs):
            fd, path = original_mkstemp(*args, **kwargs)
            if "jlink_" in path:
                created_scripts.append(path)
            return fd, path
        
        with patch("tempfile.mkstemp", side_effect=tracking_mkstemp):
            exit_code = cmd_flash(
                firmware=hex_firmware,
                chip="nrf5340",
                address=None,
                port=None,
                tool="jlink",
                baud=115200,
                connect_under_reset=False,
                device="NRF5340_XXAA_APP",
                reset_after=True,
                json_mode=True,
            )
        
        # Flash should succeed
        assert exit_code == 0
        
        # All created script files should be cleaned up
        # Note: In actual execution, cleanup happens in cmd_flash
        # In this test, we're verifying the script path was tracked


def test_cmd_flash_jlink_method_in_output(hex_firmware):
    """Test that flash method is reported as 'jlink_direct' in output."""
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = "Success"
        mock_result.stderr = ""
        mock_run.return_value = mock_result
        
        with patch("eab.cli.flash.flash_cmd._print") as mock_print:
            cmd_flash(
                firmware=hex_firmware,
                chip="nrf5340",
                address=None,
                port=None,
                tool="jlink",
                baud=115200,
                connect_under_reset=False,
                device="NRF5340_XXAA_APP",
                reset_after=True,
                json_mode=True,
            )
            
            # Check that _print was called with method='jlink_direct'
            assert mock_print.called
            call_args = mock_print.call_args[0][0]
            assert call_args["method"] == "jlink_direct"
            assert call_args["tool"] == "JLinkExe"


def test_cmd_flash_jlink_different_variants():
    """Test cmd_flash with --tool jlink works for different Zephyr variants."""
    variants = ["nrf5340", "nrf52840", "nrf52833"]
    
    with tempfile.NamedTemporaryFile(suffix=".hex", delete=False) as f:
        hex_path = f.name
        f.write(b":00000001FF\n")
    
    try:
        for variant in variants:
            with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
                mock_result = MagicMock()
                mock_result.returncode = 0
                mock_result.stdout = "Success"
                mock_result.stderr = ""
                mock_run.return_value = mock_result
                
                exit_code = cmd_flash(
                    firmware=hex_path,
                    chip=variant,
                    address=None,
                    port=None,
                    tool="jlink",
                    baud=115200,
                    connect_under_reset=False,
                    device=f"{variant.upper()}_XXAA",
                    reset_after=True,
                    json_mode=True,
                )
                
                assert exit_code == 0
    finally:
        os.unlink(hex_path)


def test_cmd_flash_jlink_handles_subprocess_error(hex_firmware):
    """Test cmd_flash with --tool jlink handles subprocess errors gracefully."""
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        mock_result = MagicMock()
        mock_result.returncode = 1
        mock_result.stdout = ""
        mock_result.stderr = "J-Link connection failed"
        mock_run.return_value = mock_result
        
        exit_code = cmd_flash(
            firmware=hex_firmware,
            chip="nrf5340",
            address=None,
            port=None,
            tool="jlink",
            baud=115200,
            connect_under_reset=False,
            device="NRF5340_XXAA_APP",
            reset_after=True,
            json_mode=True,
        )
        
        # Should return error code 1
        assert exit_code == 1


def test_cmd_flash_jlink_timeout_handling(hex_firmware):
    """Test cmd_flash with --tool jlink handles timeout errors."""
    import subprocess
    
    with patch("eab.cli.flash._execute.subprocess.run") as mock_run:
        mock_run.side_effect = subprocess.TimeoutExpired(
            cmd=["JLinkExe"],
            timeout=120.0
        )
        
        exit_code = cmd_flash(
            firmware=hex_firmware,
            chip="nrf5340",
            address=None,
            port=None,
            tool="jlink",
            baud=115200,
            connect_under_reset=False,
            device="NRF5340_XXAA_APP",
            reset_after=True,
            json_mode=True,
        )
        
        # Should handle timeout and return error
        assert exit_code == 1
