import os
import tempfile
import ctypes

from target_simulator.core.sfp_structures import SFPHeader
from target_simulator.core.sfp_transport import SfpTransport


class DummySocket:
    def __init__(self):
        self.sent = []

    def sendto(self, data, addr):
        self.sent.append((bytes(data), addr))


def make_header(flow=ord("M"), tid=1, flags=0, frag=0, total_frags=1, pl_size=0, pl_offset=0, total_size=0):
    h = SFPHeader()
    # set simple fields; other fields default to 0
    h.SFP_FLOW = flow
    h.SFP_TID = tid
    h.SFP_FLAGS = flags
    h.SFP_FRAG = frag
    h.SFP_TOTFRGAS = total_frags
    h.SFP_PLSIZE = pl_size
    h.SFP_PLOFFSET = pl_offset
    h.SFP_TOTSIZE = total_size
    return bytes(bytearray(ctypes.string_at(ctypes.addressof(h), ctypes.sizeof(h))))


def test_send_ack_sets_fields_and_win():
    dest = ("127.0.0.1", 9999)
    dsock = DummySocket()
    # create transport with ack_config for flow 'M' -> win 42
    st = SfpTransport(host="127.0.0.1", port=0, payload_handlers={}, ack_config={ord("M"): 42})
    st._socket = dsock

    hdr_bytes = bytearray(make_header(flow=ord("M"), flags=0x00))
    # place some flags value at SFP_FLAGS offset to test bit ops
    off_flags = SFPHeader.get_field_offset("SFP_FLAGS")
    hdr_bytes[off_flags] = 0x02  # bit1 set (please_resend)

    st._send_ack(dest, bytes(hdr_bytes))

    assert dsock.sent, "No ACK sent"
    sent_hdr, sent_addr = dsock.sent[0]
    assert sent_addr == dest
    # verify direction set to '<' (0x3C)
    off_dir = SFPHeader.get_field_offset("SFP_DIRECTION")
    assert sent_hdr[off_dir] == 0x3C
    # verify WIN set to 42
    off_win = SFPHeader.get_field_offset("SFP_WIN")
    assert sent_hdr[off_win] == 42
    # verify FLAGS has bit0 set and bit1 cleared per implementation
    off_flags = SFPHeader.get_field_offset("SFP_FLAGS")
    flags_after = sent_hdr[off_flags]
    assert (flags_after & 0x01) == 0x01
    assert (flags_after & 0x02) == 0


def test_process_packet_calls_handler_for_complete_single_fragment():
    captured = {}

    def handler(payload):
        captured['payload'] = bytes(payload)

    st = SfpTransport(host="127.0.0.1", port=0, payload_handlers={ord("M"): handler})
    st._socket = DummySocket()

    payload = b"HELLO"
    hdr = bytearray(make_header(flow=ord("M"), tid=7, flags=0x00, frag=0, total_frags=1, pl_size=len(payload), pl_offset=0, total_size=len(payload)))
    packet = bytes(hdr) + payload

    st._process_packet(packet, ("1.2.3.4", 1111))

    assert 'payload' in captured
    assert captured['payload'] == payload
import ctypes
import pytest

from target_simulator.core.sfp_transport import SfpTransport
from target_simulator.core.sfp_structures import SFPHeader


class DummySocket:
    def __init__(self):
        self.sent = []

    def sendto(self, data, addr):
        self.sent.append((bytes(data), addr))


def make_header_bytes(flow_char: bytes = b'M', tid: int = 1):
    hdr = SFPHeader()
    hdr.SFP_FLOW = ord(flow_char)
    hdr.SFP_TID = int(tid) & 0xFF
    # minimal sensible values for fragmentation
    hdr.SFP_FRAG = 0
    hdr.SFP_TOTFRGAS = 1
    hdr.SFP_PLSIZE = 0
    hdr.SFP_PLOFFSET = 0
    hdr.SFP_TOTSIZE = 1
    return ctypes.string_at(ctypes.addressof(hdr), ctypes.sizeof(hdr))


def test_send_ack_sets_flags_and_win():
    def dummy_handler(payload):
        pass

    st = SfpTransport('127.0.0.1', 0, payload_handlers={ord('M'): dummy_handler}, ack_config={ord('M'): 5})
    dummy_sock = DummySocket()
    st._socket = dummy_sock

    hdr_bytes = bytearray(make_header_bytes(b'M', 2))
    # ensure flags initially zero
    assert hdr_bytes[SFPHeader.get_field_offset('SFP_FLAGS')] == 0

    st._send_ack(('1.2.3.4', 9999), hdr_bytes)
    assert len(dummy_sock.sent) == 1
    sent_hdr, addr = dummy_sock.sent[0]
    # Check WIN field was set to ack_config value
    win = sent_hdr[SFPHeader.get_field_offset('SFP_WIN')]
    assert win == 5
    # Check direction changed to '<' (0x3C)
    direction = sent_hdr[SFPHeader.get_field_offset('SFP_DIRECTION')]
    assert direction == 0x3C
