// SPDX-License-Identifier: 0BSD
// Copyright (c) 2024-2026 Sudo-Ivan / Quad4.io
package transport

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"fmt"
	"net"
	"reflect"
	"sync"
	"time"

	"git.quad4.io/Networks/Reticulum-Go/pkg/announce"
	"git.quad4.io/Networks/Reticulum-Go/pkg/common"
	"git.quad4.io/Networks/Reticulum-Go/pkg/debug"
	"git.quad4.io/Networks/Reticulum-Go/pkg/destination"
	"git.quad4.io/Networks/Reticulum-Go/pkg/identity"
	"git.quad4.io/Networks/Reticulum-Go/pkg/interfaces"
	"git.quad4.io/Networks/Reticulum-Go/pkg/packet"
	"git.quad4.io/Networks/Reticulum-Go/pkg/pathfinder"
	"git.quad4.io/Networks/Reticulum-Go/pkg/rate"
)

var (
	transportInstance *Transport
	transportMutex    sync.Mutex
)

const (
	PathfinderM     = 128 // Maximum number of hops that Reticulum will transport a packet
	PathRequestTTL  = 300 // Time to live for path requests in seconds
	AnnounceTimeout = 15  // Timeout for announce responses in seconds

	// Link constants
	EstablishmentTimeoutPerHop = 6   // Timeout for link establishment per hop
	KeepaliveTimeoutFactor     = 4   // RTT timeout factor for link timeout
	StaleGrace                 = 2   // Grace period in seconds
	Keepalive                  = 360 // Interval for sending keep-alive packets
	StaleTime                  = 720 // Time after which link is considered stale

	// Resource strategies
	AcceptNone = 0
	AcceptAll  = 1
	AcceptApp  = 2

	// Resource status
	ResourceStatusPending   = 0x00
	ResourceStatusActive    = 0x01
	ResourceStatusComplete  = 0x02
	ResourceStatusFailed    = 0x03
	ResourceStatusCancelled = 0x04

	// Direction constants
	OUT = 0x02
	IN  = 0x01

	// Destination type constants
	SINGLE = 0x00
	GROUP  = 0x01
	PLAIN  = 0x02

	// Link status constants
	STATUS_NEW    = 0
	STATUS_ACTIVE = 1
	STATUS_CLOSED = 2
	STATUS_FAILED = 3

	AnnounceRatePercent = 2.0  // 2% of bandwidth for announces
	PATHFINDER_M        = 8    // Maximum hop count
	AnnounceRateKbps    = 20.0 // 20 Kbps for announces

	MAX_HOPS         = 128  // Default m value for announce propagation
	PROPAGATION_RATE = 0.02 // 2% bandwidth cap for announces

	// Announce packet types
	PACKET_TYPE_ANNOUNCE = 0x01
	PACKET_TYPE_LINK     = 0x02

	// Announce flags
	ANNOUNCE_NONE     = 0x00
	ANNOUNCE_PATH     = 0x01
	ANNOUNCE_IDENTITY = 0x02

	// Header types
	HEADER_TYPE_1 = 0x00 // One address field
	HEADER_TYPE_2 = 0x01 // Two address fields

	// Propagation types
	PROP_TYPE_BROADCAST = 0x00
	PROP_TYPE_TRANSPORT = 0x01

	// Destination types
	DEST_TYPE_SINGLE = 0x00
	DEST_TYPE_GROUP  = 0x01
	DEST_TYPE_PLAIN  = 0x02
	DEST_TYPE_LINK   = 0x03
)

type PathInfo struct {
	NextHop     []byte
	Interface   string
	Hops        uint8
	LastUpdated time.Time
}

type Transport struct {
	mutex                 sync.RWMutex
	config                *common.ReticulumConfig
	interfaces            map[string]common.NetworkInterface
	links                 map[string]LinkInterface
	destinations          map[string]interface{}
	announceRate          *rate.Limiter
	seenAnnounces         map[string]bool
	pathfinder            *pathfinder.PathFinder
	announceHandlers      []announce.Handler
	paths                 map[string]*common.Path
	receipts              []*packet.PacketReceipt
	receiptsMutex         sync.RWMutex
	pathRequests          map[string]time.Time
	pathStates            map[string]byte
	discoveryPathRequests map[string]*DiscoveryPathRequest
	discoveryPRTags       map[string]bool
	announceTable         map[string]*PathAnnounceEntry
	heldAnnounces         map[string]*PathAnnounceEntry
	transportIdentity     *identity.Identity
	pathRequestDest       interface{}
	done                  chan struct{}
	stopOnce              sync.Once
}

type DiscoveryPathRequest struct {
	DestinationHash []byte
	Timeout         time.Time
	RequestingIface common.NetworkInterface
}

type PathAnnounceEntry struct {
	CreatedAt         time.Time
	RetransmitTimeout time.Time
	Retries           int
	ReceivedFrom      common.NetworkInterface
	AnnounceHops      byte
	Packet            *packet.Packet
	LocalRebroadcasts int
	BlockRebroadcasts bool
	AttachedInterface common.NetworkInterface
}

const (
	STATE_UNKNOWN      = 0x00
	STATE_UNRESPONSIVE = 0x01
	STATE_RESPONSIVE   = 0x02
)

type Path struct {
	NextHop   []byte
	Interface common.NetworkInterface
	HopCount  byte
}

func NewTransport(cfg *common.ReticulumConfig) *Transport {
	t := &Transport{
		interfaces:            make(map[string]common.NetworkInterface),
		paths:                 make(map[string]*common.Path), // TODO: Load persisted path table from storage/destination_table for faster startup
		seenAnnounces:         make(map[string]bool),
		announceRate:          rate.NewLimiter(rate.DefaultBurstFreq, 20.0),
		mutex:                 sync.RWMutex{},
		config:                cfg,
		links:                 make(map[string]LinkInterface),
		destinations:          make(map[string]interface{}),
		pathfinder:            pathfinder.NewPathFinder(),
		receipts:              make([]*packet.PacketReceipt, common.ZERO),
		receiptsMutex:         sync.RWMutex{},
		pathRequests:          make(map[string]time.Time),
		pathStates:            make(map[string]byte),
		discoveryPathRequests: make(map[string]*DiscoveryPathRequest),
		discoveryPRTags:       make(map[string]bool),
		announceTable:         make(map[string]*PathAnnounceEntry),
		heldAnnounces:         make(map[string]*PathAnnounceEntry),
		done:                  make(chan struct{}),
	}

	// TODO: Path table persistence

	transportIdent, err := identity.LoadOrCreateTransportIdentity()
	if err == nil {
		t.transportIdentity = transportIdent
	}

	go t.startMaintenanceJobs()

	return t
}

func (t *Transport) startMaintenanceJobs() {
	ticker := time.NewTicker(common.FIVE * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			t.cleanupExpiredPaths()
			t.cleanupExpiredDiscoveryRequests()
			t.cleanupExpiredAnnounces()
			t.cleanupExpiredReceipts()
		case <-t.done:
			return
		}
	}
}

func (t *Transport) cleanupExpiredPaths() {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	now := time.Now()
	pathExpiry := 7 * 24 * time.Hour

	for destHash, path := range t.paths {
		if now.Sub(path.LastUpdated) > pathExpiry {
			delete(t.paths, destHash)
			delete(t.pathStates, destHash)
			debug.Log(debug.DEBUG_VERBOSE, "Expired path", "dest_hash", fmt.Sprintf("%x", destHash[:common.EIGHT]))
		}
	}
}

func (t *Transport) cleanupExpiredDiscoveryRequests() {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	now := time.Now()
	for destHash, req := range t.discoveryPathRequests {
		if now.After(req.Timeout) {
			delete(t.discoveryPathRequests, destHash)
			debug.Log(debug.DEBUG_VERBOSE, "Expired discovery path request", "dest_hash", fmt.Sprintf("%x", destHash[:common.EIGHT]))
		}
	}
}

func (t *Transport) cleanupExpiredAnnounces() {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	announceExpiry := 24 * time.Hour

	for destHash, entry := range t.announceTable {
		if entry != nil && time.Since(entry.CreatedAt) > announceExpiry {
			delete(t.announceTable, destHash)
			debug.Log(debug.DEBUG_VERBOSE, "Expired announce entry", "dest_hash", fmt.Sprintf("%x", destHash[:common.EIGHT]))
		}
	}

	for destHash, entry := range t.heldAnnounces {
		if entry != nil && time.Since(entry.CreatedAt) > announceExpiry {
			delete(t.heldAnnounces, destHash)
		}
	}
}

func (t *Transport) cleanupExpiredReceipts() {
	t.receiptsMutex.Lock()
	defer t.receiptsMutex.Unlock()

	validReceipts := make([]*packet.PacketReceipt, common.ZERO)
	for _, receipt := range t.receipts {
		if receipt != nil && !receipt.IsTimedOut() {
			status := receipt.GetStatus()
			if status == packet.RECEIPT_SENT || status == packet.RECEIPT_DELIVERED {
				validReceipts = append(validReceipts, receipt)
			}
		}
	}

	if len(validReceipts) < len(t.receipts) {
		t.receipts = validReceipts
		debug.Log(debug.DEBUG_VERBOSE, "Cleaned up expired receipts", "remaining", len(validReceipts))
	}
}

func (t *Transport) MarkPathUnresponsive(destHash []byte) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.pathStates[string(destHash)] = STATE_UNRESPONSIVE
}

func (t *Transport) MarkPathResponsive(destHash []byte) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.pathStates[string(destHash)] = STATE_RESPONSIVE
}

func (t *Transport) PathIsUnresponsive(destHash []byte) bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	state, exists := t.pathStates[string(destHash)]
	return exists && state == STATE_UNRESPONSIVE
}

// RegisterDestination registers a destination to receive incoming link requests
func (t *Transport) RegisterDestination(hash []byte, dest interface{}) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.destinations[string(hash)] = dest
	debug.Log(debug.DEBUG_TRACE, "Registered destination with transport", "hash", fmt.Sprintf("%x", hash))
}

// CreateIncomingLink creates a link object for an incoming link request
// This avoids circular import issues by having transport create the link
func (t *Transport) CreateIncomingLink(dest interface{}, networkIface common.NetworkInterface) interface{} {
	// This function signature uses interface{} to avoid importing link package
	// The actual implementation will be in the application code
	// For now, return nil to indicate links aren't fully implemented
	debug.Log(debug.DEBUG_TRACE, "CreateIncomingLink called (not yet fully implemented)")
	return nil
}

func GetTransportInstance() *Transport {
	transportMutex.Lock()
	defer transportMutex.Unlock()
	return transportInstance
}

func (t *Transport) RegisterInterface(name string, iface common.NetworkInterface) error {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if _, exists := t.interfaces[name]; exists {
		return errors.New("interface already registered")
	}

	t.interfaces[name] = iface
	return nil
}

func (t *Transport) GetInterface(name string) (common.NetworkInterface, error) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	iface, exists := t.interfaces[name]
	if !exists {
		return nil, errors.New("interface not found")
	}

	return iface, nil
}

func (t *Transport) Close() error {
	t.stopOnce.Do(func() {
		close(t.done)
	})

	t.mutex.Lock()
	defer t.mutex.Unlock()

	for _, iface := range t.interfaces {
		iface.Detach()
	}

	return nil
}

type Link struct {
	mutex               sync.RWMutex
	destination         []byte
	establishedAt       time.Time
	lastInbound         time.Time
	lastOutbound        time.Time
	lastData            time.Time
	rtt                 time.Duration
	establishedCb       func()
	closedCb            func()
	packetCb            func([]byte, *packet.Packet)
	resourceCb          func(interface{}) bool
	resourceStrategy    int
	resourceStartedCb   func(interface{})
	resourceConcludedCb func(interface{})
	remoteIdentifiedCb  func(*Link, []byte)
	connectedCb         func()
	disconnectedCb      func()
	remoteIdentity      []byte
	physicalStats       bool
	staleTime           time.Duration
	staleGrace          time.Duration
	status              int
}

type Destination struct {
	Identity  interface{}
	Direction int
	Type      int
	AppName   string
	Aspects   []string
}

func NewLink(dest []byte, establishedCallback func(), closedCallback func()) *Link {
	return &Link{
		destination:   dest,
		establishedAt: time.Now(),
		lastInbound:   time.Now(),
		lastOutbound:  time.Now(),
		lastData:      time.Now(),
		establishedCb: establishedCallback,
		closedCb:      closedCallback,
		staleTime:     time.Duration(StaleTime) * time.Second,
		staleGrace:    time.Duration(StaleGrace) * time.Second,
	}
}

// Link methods
func (l *Link) GetAge() time.Duration {
	return time.Since(l.establishedAt)
}

func (l *Link) NoInboundFor() time.Duration {
	return time.Since(l.lastInbound)
}

func (l *Link) NoOutboundFor() time.Duration {
	return time.Since(l.lastOutbound)
}

func (l *Link) NoDataFor() time.Duration {
	return time.Since(l.lastData)
}

func (l *Link) InactiveFor() time.Duration {
	inbound := l.NoInboundFor()
	outbound := l.NoOutboundFor()
	if inbound < outbound {
		return inbound
	}
	return outbound
}

func (l *Link) SetPacketCallback(cb func([]byte, *packet.Packet)) {
	l.packetCb = cb
}

func (l *Link) SetResourceCallback(cb func(interface{}) bool) {
	l.resourceCb = cb
}

func (l *Link) Teardown() {
	if l.disconnectedCb != nil {
		l.disconnectedCb()
	}
	if l.closedCb != nil {
		l.closedCb()
	}
}

func (l *Link) Send(data []byte) interface{} {
	l.mutex.Lock()
	l.lastOutbound = time.Now()
	l.lastData = time.Now()
	l.mutex.Unlock()

	packet := &LinkPacket{
		Destination: l.destination,
		Data:        data,
		Timestamp:   time.Now(),
	}

	if l.rtt == common.FLOAT_ZERO {
		l.rtt = l.InactiveFor()
	}

	err := packet.send()
	if err != nil {
		return nil
	}

	return packet
}

func (t *Transport) RegisterAnnounceHandler(handler announce.Handler) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.announceHandlers = append(t.announceHandlers, handler)
}

func (t *Transport) UnregisterAnnounceHandler(handler announce.Handler) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	for i, h := range t.announceHandlers {
		if h == handler {
			t.announceHandlers = append(t.announceHandlers[:i], t.announceHandlers[i+1:]...)
			break
		}
	}
}

func (t *Transport) notifyAnnounceHandlers(destHash []byte, identity interface{}, appData []byte, hops uint8) {
	t.mutex.RLock()
	handlers := make([]announce.Handler, len(t.announceHandlers))
	copy(handlers, t.announceHandlers)
	t.mutex.RUnlock()

	for _, handler := range handlers {
		if err := handler.ReceivedAnnounce(destHash, identity, appData, hops); err != nil {
			debug.Log(debug.DEBUG_ERROR, "Error in announce handler", "error", err)
		}
	}
}

func (t *Transport) HasPath(destinationHash []byte) bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	path, exists := t.paths[string(destinationHash)]
	if !exists {
		return false
	}

	// Check if path is still valid (not expired)
	if time.Since(path.LastUpdated) > time.Duration(PathRequestTTL)*time.Second {
		delete(t.paths, string(destinationHash))
		return false
	}

	return true
}

func (t *Transport) HopsTo(destinationHash []byte) uint8 {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	path, exists := t.paths[string(destinationHash)]
	if !exists {
		return PathfinderM
	}

	return path.HopCount
}

func (t *Transport) NextHop(destinationHash []byte) []byte {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	path, exists := t.paths[string(destinationHash)]
	if !exists {
		return nil
	}

	return path.NextHop
}

func (t *Transport) NextHopInterface(destinationHash []byte) string {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	path, exists := t.paths[string(destinationHash)]
	if !exists {
		return ""
	}

	return path.Interface.GetName()
}

func (t *Transport) RequestPath(destinationHash []byte, onInterface string, tag []byte, recursive bool) error {
	if tag == nil {
		tag = make([]byte, common.SIZE_16)
		if _, err := rand.Read(tag); err != nil {
			return fmt.Errorf("failed to generate random tag: %w", err)
		}
	}

	var pathRequestData []byte
	if t.transportIdentity != nil {
		pathRequestData = append(destinationHash, t.transportIdentity.Hash()...)
		pathRequestData = append(pathRequestData, tag...)
	} else {
		pathRequestData = append(destinationHash, tag...)
	}

	destHashFull := sha256.Sum256([]byte("rnstransport.path.request"))
	pathRequestDestHash := destHashFull[:common.SIZE_16]

	pkt := packet.NewPacket(
		packet.DestinationPlain,
		pathRequestData,
		0x00,
		0x00,
		packet.PropagationBroadcast,
		0x01,
		pathRequestDestHash,
		false,
		0x00,
	)

	if err := pkt.Pack(); err != nil {
		return fmt.Errorf("failed to pack path request: %w", err)
	}

	debug.Log(debug.DEBUG_INFO, "Sending path request", "dest_hash", fmt.Sprintf("%x", destinationHash), "data_len", len(pathRequestData), "packet_len", len(pkt.Raw))

	if onInterface != "" {
		iface, ok := t.interfaces[onInterface]
		if !ok {
			return fmt.Errorf("interface not found: %s", onInterface)
		}
		return iface.Send(pkt.Raw, "")
	}

	for _, iface := range t.interfaces {
		if !iface.IsEnabled() {
			continue
		}
		if err := iface.Send(pkt.Raw, ""); err != nil {
			debug.Log(debug.DEBUG_ERROR, "Failed to send path request on interface", "interface", iface.GetName(), "error", err)
		}
	}

	return nil
}

func (t *Transport) updatePathUnlocked(destinationHash []byte, nextHop []byte, interfaceName string, hops uint8) {
	// Direct access to interfaces map since caller holds the lock
	iface, exists := t.interfaces[interfaceName]
	if !exists {
		debug.Log(debug.DEBUG_INFO, "Interface not found", "name", interfaceName)
		return
	}

	t.paths[string(destinationHash)] = &common.Path{
		NextHop:     nextHop,
		Interface:   iface,
		Hops:        hops,
		HopCount:    hops,
		LastUpdated: time.Now(),
	}
}

func (t *Transport) UpdatePath(destinationHash []byte, nextHop []byte, interfaceName string, hops uint8) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.updatePathUnlocked(destinationHash, nextHop, interfaceName, hops)
}

func (t *Transport) HandleAnnounce(data []byte, sourceIface common.NetworkInterface) error {
	if len(data) < 53 { // Minimum size for announce packet
		return fmt.Errorf("announce packet too small: %d bytes", len(data))
	}

	debug.Log(debug.DEBUG_ALL, "Transport handling announce", "bytes", len(data), "source", sourceIface.GetName())

	// Parse announce fields according to RNS spec
	destHash := data[common.ONE : common.SIZE_32+common.ONE]
	identity := data[common.SIZE_32+common.ONE : common.SIZE_16+common.SIZE_32+common.ONE]
	appData := data[common.SIZE_16+common.SIZE_32+common.ONE:]

	// Generate announce hash to check for duplicates
	// We exclude the hop count (byte 1) from the hash since it changes during propagation
	// We also exclude the header (byte 0) just in case propagation flags change
	// The destination hash (bytes 2-18) + payload (including random hash) is unique enough
	hashData := data[common.TWO:]
	announceHash := sha256.Sum256(hashData)
	hashStr := string(announceHash[:])

	t.mutex.Lock()
	if _, seen := t.seenAnnounces[hashStr]; seen {
		t.mutex.Unlock()
		debug.Log(debug.DEBUG_ALL, "Ignoring duplicate announce", "hash", fmt.Sprintf("%x", announceHash[:common.EIGHT]))
		return nil
	}
	t.seenAnnounces[hashStr] = true
	t.mutex.Unlock()

	// Don't forward if max hops reached
	if data[common.ZERO] >= MAX_HOPS {
		debug.Log(debug.DEBUG_ALL, "Announce exceeded max hops", "hops", data[common.ZERO])
		return nil
	}

	// Add random delay before retransmission (0-2 seconds)
	var delay time.Duration
	b := make([]byte, 8)
	_, err := rand.Read(b)
	if err != nil {
		debug.Log(debug.DEBUG_ALL, "Failed to generate random delay", "error", err)
		delay = time.Duration(0) // Default to no delay on error
	} else {
		delay = time.Duration(binary.BigEndian.Uint64(b)%2000) * time.Millisecond // #nosec G115
	}
	time.Sleep(delay)

	// Check bandwidth allocation for announces
	if !t.announceRate.Allow() {
		debug.Log(debug.DEBUG_ALL, "Announce rate limit exceeded, queuing")
		return nil
	}

	// Increment hop count
	data[0]++

	// Broadcast to all other interfaces
	var lastErr error
	for name, iface := range t.interfaces {
		if iface == sourceIface || !iface.IsEnabled() {
			continue
		}

		// Check interface-level bandwidth before forwarding
		if !iface.GetBandwidthAvailable() {
			debug.Log(debug.DEBUG_VERBOSE, "Skipping announce forwarding on interface due to bandwidth cap", "name", name)
			continue
		}

		debug.Log(debug.DEBUG_ALL, "Forwarding announce on interface", "name", name)
		if err := iface.Send(data, ""); err != nil {
			debug.Log(debug.DEBUG_ALL, "Failed to forward announce", "name", name, "error", err)
			lastErr = err
		}
	}

	// Notify handlers
	t.notifyAnnounceHandlers(destHash, identity, appData, data[0])

	return lastErr
}

func (t *Transport) NewDestination(identity interface{}, direction int, destType int, appName string, aspects ...string) *Destination {
	return &Destination{
		Identity:  identity,
		Direction: direction,
		Type:      destType,
		AppName:   appName,
		Aspects:   aspects,
	}
}

func (t *Transport) NewLink(dest []byte, establishedCallback func(), closedCallback func()) *Link {
	return NewLink(dest, establishedCallback, closedCallback)
}

type PathRequest struct {
	DestinationHash []byte
	Tag             []byte
	TTL             int
	Recursive       bool
}

type LinkPacket struct {
	Destination []byte
	Data        []byte
	Timestamp   time.Time
}

func (p *LinkPacket) send() error {
	// Get transport instance
	t := GetTransportInstance()

	// Create packet header
	header := make([]byte, 0, 64)
	header = append(header, 0x02) // Link packet type
	header = append(header, p.Destination...)

	ts := make([]byte, 8)
	binary.BigEndian.PutUint64(ts, uint64(p.Timestamp.Unix())) // #nosec G115
	header = append(header, ts...)

	// Combine header and data
	packet := append(header, p.Data...)

	// Get next hop info
	nextHop := t.NextHop(p.Destination)
	if nextHop == nil {
		return errors.New("no path to destination")
	}

	// Get interface for next hop
	ifaceName := t.NextHopInterface(p.Destination)
	iface, ok := t.interfaces[ifaceName]
	if !ok {
		return errors.New("interface not found")
	}

	// Send packet using interface's Send method
	return iface.Send(packet, "")
}

func (t *Transport) sendPathRequest(req *PathRequest, interfaceName string) error {
	// Create path request packet
	packet := &PathRequestPacket{
		Type:            0x01,
		DestinationHash: req.DestinationHash,
		Tag:             req.Tag,
		TTL:             byte(req.TTL),
		Recursive:       req.Recursive,
	}

	// Serialize packet
	buf := make([]byte, 0, 128)
	buf = append(buf, packet.Type)
	buf = append(buf, packet.DestinationHash...)
	buf = append(buf, packet.Tag...)
	buf = append(buf, packet.TTL)
	if packet.Recursive {
		buf = append(buf, 0x01)
	} else {
		buf = append(buf, 0x00)
	}

	// Get interface
	iface, ok := t.interfaces[interfaceName]
	if !ok {
		return errors.New("interface not found")
	}

	return iface.Send(buf, "")
}

func (t *Transport) broadcastPathRequest(req *PathRequest) error {
	var lastErr error
	for _, iface := range t.interfaces {
		if !iface.IsEnabled() {
			continue
		}

		if err := t.sendPathRequest(req, iface.GetName()); err != nil {
			lastErr = err
		}
	}
	return lastErr
}

type PathRequestPacket struct {
	Type            byte   // 0x01 for path request
	DestinationHash []byte // 32 bytes
	Tag             []byte // Variable length
	TTL             byte
	Recursive       bool
}

type NetworkInterface struct {
	Name    string
	Addr    *net.UDPAddr
	Conn    *net.UDPConn
	MTU     int
	Enabled bool
}

func SendAnnounce(packet []byte) error {
	t := GetTransportInstance()
	if t == nil {
		return errors.New("transport not initialized")
	}

	// Send announce packet to all interfaces
	var lastErr error
	for _, iface := range t.interfaces {
		if err := iface.Send(packet, ""); err != nil {
			lastErr = err
		}
	}

	return lastErr
}

func (t *Transport) HandlePacket(data []byte, iface common.NetworkInterface) {
	if len(data) < common.TWO {
		debug.Log(debug.DEBUG_INFO, "Dropping packet: insufficient length", common.STR_BYTES, len(data))
		return
	}

	headerByte := data[common.ZERO]
	packetType := headerByte & common.HEX_0x03
	headerType := (headerByte & 0x40) >> common.SIX
	contextFlag := (headerByte & 0x20) >> common.FIVE
	propType := (headerByte & 0x10) >> common.FOUR
	destType := (headerByte & 0x0C) >> common.TWO

	debug.Log(debug.DEBUG_INFO, "TRANSPORT: Packet received", "type", fmt.Sprintf(common.STR_FMT_HEX, packetType), "header", headerType, "context", contextFlag, "propType", propType, "destType", destType, "size", len(data))
	debug.Log(debug.DEBUG_TRACE, "Interface and raw header", "name", iface.GetName(), "header", fmt.Sprintf(common.STR_FMT_HEX, headerByte))

	if len(data) == common.SIXTY_SEVEN {
		debug.Log(debug.DEBUG_ERROR, "67-byte packet detected", "header", fmt.Sprintf(common.STR_FMT_HEX, headerByte), "packet_type_bits", fmt.Sprintf(common.STR_FMT_HEX, packetType), "first_32_bytes", fmt.Sprintf("%x", data[:common.SIZE_32]))
	}

	if tcpIface, ok := iface.(*interfaces.TCPClientInterface); ok {
		tcpIface.UpdateStats(uint64(len(data)), true)
		debug.Log(debug.DEBUG_PACKETS, "Updated TCP interface stats", "rx_bytes", len(data))
	}

	dataCopy := make([]byte, len(data))
	copy(dataCopy, data)

	go func() {
		switch packetType {
		case PACKET_TYPE_ANNOUNCE:
			debug.Log(debug.DEBUG_VERBOSE, "Processing announce packet")
			if err := t.handleAnnouncePacket(dataCopy, iface); err != nil {
				debug.Log(debug.DEBUG_INFO, "Announce handling failed", "error", err)
			}
		case PACKET_TYPE_LINK:
			debug.Log(debug.DEBUG_ERROR, "Processing link packet (type=0x02)", "packet_size", len(dataCopy))
			t.handleLinkPacket(dataCopy[common.ONE:], iface, PACKET_TYPE_LINK)
		case packet.PacketTypeProof:
			debug.Log(debug.DEBUG_VERBOSE, "Processing proof packet")
			fullData := append([]byte{packet.PacketTypeProof}, dataCopy[common.ONE:]...)
			pkt := &packet.Packet{Raw: fullData}
			if err := pkt.Unpack(); err != nil {
				debug.Log(debug.DEBUG_INFO, "Failed to unpack proof packet", "error", err)
				return
			}
			t.handleProofPacket(pkt, iface)
		case common.ZERO:
			// Data packets addressed to link destinations carry link traffic
			if destType == DEST_TYPE_LINK {
				debug.Log(debug.DEBUG_ERROR, "Processing link data packet (dest_type=3)", "packet_size", len(dataCopy))
				t.handleLinkPacket(dataCopy[common.ONE:], iface, common.ZERO)
			} else {
				debug.Log(debug.DEBUG_ERROR, "Processing data packet (type 0x00)", "packet_size", len(dataCopy), "dest_type", destType, "header_type", headerType)
				t.handleTransportPacket(dataCopy[common.ONE:], iface)
			}
		default:
			debug.Log(debug.DEBUG_INFO, "Unknown packet type", "type", fmt.Sprintf(common.STR_FMT_HEX, packetType), "source", iface.GetName())
		}
	}()
}

func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterface) error {
	debug.Log(debug.DEBUG_INFO, "Processing announce packet", "length", len(data))
	if len(data) < common.TWO {
		return fmt.Errorf("packet too small for header")
	}

	// Parse header bytes according to RNS spec
	headerByte1 := data[common.ZERO]
	hopCount := data[common.ONE]

	// Extract header fields
	ifacFlag := (headerByte1 & 0x80) >> common.SEVEN   // IFAC flag in highest bit
	headerType := (headerByte1 & 0x40) >> common.SIX   // Header type in next bit
	contextFlag := (headerByte1 & 0x20) >> common.FIVE // Context flag
	propType := (headerByte1 & 0x10) >> common.FOUR    // Propagation type
	destType := (headerByte1 & 0x0C) >> common.TWO     // Destination type in next 2 bits
	packetType := headerByte1 & common.HEX_0x03        // Packet type in lowest 2 bits

	debug.Log(debug.DEBUG_TRACE, "Announce header", "ifac", ifacFlag, "headerType", headerType, "context", contextFlag, "propType", propType, "destType", destType, "packetType", packetType)

	// Skip IFAC code if present
	startIdx := common.TWO
	if ifacFlag == common.ONE {
		startIdx++ // For now assume 1 byte IFAC code
	}

	// Announce packets use HEADER_TYPE_1 (single address field)
	// Calculate address field size
	addrSize := common.SIZE_16 // Always 16 bytes for HEADER_TYPE_1
	if headerType == common.ONE {
		// HEADER_TYPE_2 has two address fields
		addrSize = common.SIZE_32
	}

	// Validate minimum packet size
	minSize := startIdx + addrSize + common.ONE // Header + address(es) + context
	if len(data) < minSize {
		return fmt.Errorf("packet too small: %d bytes", len(data))
	}

	// Extract fields based on header type
	var destinationHash []byte
	var context byte
	var payload []byte

	if headerType == 0 {
		// HEADER_TYPE_1: Header(2) + DestHash(16) + Context(1) + Data
		destinationHash = data[startIdx : startIdx+16]
		context = data[startIdx+16]
		payload = data[startIdx+17:]
	} else {
		// HEADER_TYPE_2: Header(2) + TransportID(16) + DestHash(16) + Context(1) + Data
		// Skip transport ID, get destination hash
		destinationHash = data[startIdx+16 : startIdx+32]
		context = data[startIdx+32]
		payload = data[startIdx+33:]
	}

	debug.Log(debug.DEBUG_INFO, "Destination hash", "hash", fmt.Sprintf("%x", destinationHash))
	debug.Log(debug.DEBUG_INFO, "Context and payload", "context", fmt.Sprintf("%02x", context), "payload_len", len(payload))
	debug.Log(debug.DEBUG_INFO, "Packet total length", "length", len(data))

	// Parse announce packet according to RNS specification
	// Announce packets have the format:
	// [Public Key (64)][Name Hash (10)][Random Hash (10)][Ratchet (0 or 32)][Signature (64)][App Data]
	// Ratchet is present if context flag is set

	var id *identity.Identity
	var appData []byte
	var pubKey []byte

	minAnnounceSize := 64 + 10 + 10 + 64 // pubKey + nameHash + randomHash + signature
	if len(payload) < minAnnounceSize {
		debug.Log(debug.DEBUG_INFO, "Payload too small for announce", "bytes", len(payload), "minimum", minAnnounceSize)
		return fmt.Errorf("payload too small for announce")
	}

	// Parse the announce data
	pos := 0
	pubKey = payload[pos : pos+64] // 64 bytes: encKey (32) + signKey (32)
	pos += 64
	nameHash := payload[pos : pos+10]
	pos += 10
	randomHash := payload[pos : pos+10]
	pos += 10

	// Check if there's a ratchet based on context flag
	var ratchetData []byte
	if contextFlag == 1 {
		// Context flag is set, ratchet is present
		if len(payload) < pos+32+64 {
			debug.Log(debug.DEBUG_INFO, "Payload too small for announce with ratchet")
			return fmt.Errorf("payload too small for announce with ratchet")
		}
		ratchetData = payload[pos : pos+32]
		pos += 32
	}

	signature := payload[pos : pos+64]
	pos += 64
	appData = payload[pos:]

	ratchetHex := ""
	if len(ratchetData) > 0 {
		ratchetHex = fmt.Sprintf("%x", ratchetData[:8])
	} else {
		ratchetHex = "(none)"
	}
	debug.Log(debug.DEBUG_INFO, "Parsed announce", "pubKey", fmt.Sprintf("%x", pubKey[:8]), "nameHash", fmt.Sprintf("%x", nameHash), "randomHash", fmt.Sprintf("%x", randomHash), "ratchet", ratchetHex, "appData_len", len(appData))

	// Create identity from public key
	id = identity.FromPublicKey(pubKey)
	if id == nil {
		debug.Log(debug.DEBUG_INFO, "Failed to create identity from public key")
		return fmt.Errorf("invalid identity")
	}
	debug.Log(debug.DEBUG_INFO, "Successfully created identity")

	// Build signature data:
	// destination_hash + public_key + name_hash + random_hash + ratchet (if present) + app_data
	signData := make([]byte, 0)
	signData = append(signData, destinationHash...) // destination hash from packet header
	signData = append(signData, pubKey...)
	signData = append(signData, nameHash...)
	signData = append(signData, randomHash...)
	if len(ratchetData) > 0 {
		signData = append(signData, ratchetData...)
	}
	signData = append(signData, appData...)

	debug.Log(debug.DEBUG_INFO, "Verifying signature", "data_len", len(signData))

	// Verify signature
	if !id.Verify(signData, signature) {
		debug.Log(debug.DEBUG_INFO, "Signature verification failed - announce rejected")
		return fmt.Errorf("invalid announce signature")
	}
	debug.Log(debug.DEBUG_INFO, "Signature verification successful")

	// Validate destination hash according to RNS spec:
	// expected_hash = SHA256(name_hash + identity_hash)[:16]
	hashMaterial := make([]byte, 0)
	hashMaterial = append(hashMaterial, nameHash...)  // Name hash (10 bytes) first
	hashMaterial = append(hashMaterial, id.Hash()...) // Identity hash (16 bytes) second
	expectedHashFull := sha256.Sum256(hashMaterial)
	expectedHash := expectedHashFull[:16]

	debug.Log(debug.DEBUG_INFO, "Destination hash validation", "received", fmt.Sprintf("%x", destinationHash), "expected", fmt.Sprintf("%x", expectedHash))

	if string(destinationHash) != string(expectedHash) {
		debug.Log(debug.DEBUG_INFO, "Destination hash mismatch - announce rejected")
		return fmt.Errorf("destination hash mismatch")
	}
	debug.Log(debug.DEBUG_INFO, "Destination hash validation successful")

	// Log app_data content for accepted announces
	if len(appData) > 0 {
		debug.Log(debug.DEBUG_INFO, "Accepted announce with app_data", "data", fmt.Sprintf("%x", appData), "string", string(appData))
	}

	// Store the identity for later recall
	identity.Remember(data, destinationHash, pubKey, appData)

	// Generate announce hash to check for duplicates
	// We exclude the hop count (byte 1) from the hash since it changes during propagation
	// We also exclude the header (byte 0) just in case propagation flags change
	// The destination hash (bytes 2-18) + payload (including random hash) is unique enough
	hashData := data[common.TWO:]
	announceHash := sha256.Sum256(hashData)
	hashStr := string(announceHash[:])

	debug.Log(debug.DEBUG_INFO, "Announce hash", "hash", fmt.Sprintf("%x", announceHash[:8]))

	t.mutex.Lock()
	if _, seen := t.seenAnnounces[hashStr]; seen {
		t.mutex.Unlock()
		debug.Log(debug.DEBUG_INFO, "Ignoring duplicate announce", "hash", fmt.Sprintf("%x", announceHash[:8]))
		return nil
	}
	t.seenAnnounces[hashStr] = true
	t.mutex.Unlock()

	debug.Log(debug.DEBUG_INFO, "Processing new announce")

	// Register the path from this announce
	// The destination is reachable via the interface that received this announce
	if iface != nil {
		// Use unlocked version since we may be called in a locked context
		t.mutex.Lock()
		t.updatePathUnlocked(destinationHash, nil, iface.GetName(), hopCount)
		t.mutex.Unlock()
		debug.Log(debug.DEBUG_INFO, "Registered path", "hash", fmt.Sprintf("%x", destinationHash), "interface", iface.GetName(), "hops", hopCount)
	}

	// Notify handlers first, regardless of forwarding limits
	debug.Log(debug.DEBUG_INFO, "Notifying announce handlers", "destHash", fmt.Sprintf("%x", destinationHash), "appDataLen", len(appData))
	t.notifyAnnounceHandlers(destinationHash, id, appData, hopCount)
	debug.Log(debug.DEBUG_INFO, "Announce handlers notified")

	// Don't forward if max hops reached
	if hopCount >= MAX_HOPS {
		debug.Log(debug.DEBUG_INFO, "Announce exceeded max hops", "hops", hopCount)
		return nil
	}
	debug.Log(debug.DEBUG_INFO, "Hop count OK", "hops", hopCount)

	// Check bandwidth allocation for announces
	if !t.announceRate.Allow() {
		debug.Log(debug.DEBUG_INFO, "Announce rate limit exceeded, not forwarding")
		return nil
	}
	debug.Log(debug.DEBUG_INFO, "Bandwidth check passed")

	// Add random delay before retransmission (0-2 seconds)
	var delay time.Duration
	b := make([]byte, 8)
	_, err := rand.Read(b)
	if err != nil {
		debug.Log(debug.DEBUG_ALL, "Failed to generate random delay", "error", err)
		delay = time.Duration(0) // Default to no delay on error
	} else {
		delay = time.Duration(binary.BigEndian.Uint64(b)%2000) * time.Millisecond // #nosec G115
	}
	time.Sleep(delay)

	// Increment hop count
	data[1]++

	// Broadcast to all other interfaces
	var lastErr error
	for name, outIface := range t.interfaces {
		if outIface == iface || !outIface.IsEnabled() {
			continue
		}

		// Check interface-level bandwidth before forwarding
		if !outIface.GetBandwidthAvailable() {
			debug.Log(debug.DEBUG_VERBOSE, "Skipping announce forwarding on interface due to bandwidth cap", "name", name)
			continue
		}

		debug.Log(debug.DEBUG_ALL, "Forwarding announce on interface", "name", name)
		if err := outIface.Send(data, ""); err != nil {
			debug.Log(debug.DEBUG_ALL, "Failed to forward announce", "name", name, "error", err)
			lastErr = err
		}
	}

	return lastErr
}

func (t *Transport) handleLinkPacket(data []byte, iface common.NetworkInterface, packetType byte) {
	startTime := time.Now()
	debug.Log(debug.DEBUG_INFO, "Handling link packet", "bytes", len(data), "packet_type", fmt.Sprintf("0x%02x", packetType), "interface", iface.GetName())

	pkt := &packet.Packet{}

	// If this is a LINKREQUEST packet (type=0x02), handle it as link establishment
	if packetType == PACKET_TYPE_LINK {
		debug.Log(debug.DEBUG_INFO, "Processing LINKREQUEST (type=0x02)", "interface", iface.GetName())

		// Parse as LINKREQUEST packet - prepend the packet type
		pkt.Raw = append([]byte{PACKET_TYPE_LINK}, data...)
		if err := pkt.Unpack(); err != nil {
			debug.Log(debug.DEBUG_ERROR, "Failed to unpack link request", "error", err, "elapsed", time.Since(startTime).Seconds())
			return
		}

		destHash := pkt.DestinationHash
		if len(destHash) > 16 {
			destHash = destHash[:16]
		}

		debug.Log(debug.DEBUG_INFO, "Link request for destination", "hash", fmt.Sprintf("%x", destHash), "interface", iface.GetName())

		// Look up the destination
		t.mutex.RLock()
		destIface, exists := t.destinations[string(destHash)]
		t.mutex.RUnlock()

		if !exists {
			debug.Log(debug.DEBUG_ERROR, "No destination registered for hash", "hash", fmt.Sprintf("%x", destHash), "elapsed", time.Since(startTime).Seconds())
			return
		}

		debug.Log(debug.DEBUG_INFO, "Found registered destination", "hash", fmt.Sprintf("%x", destHash), "elapsed", time.Since(startTime).Seconds())

		// Handle the incoming link request
		reqStartTime := time.Now()
		t.handleIncomingLinkRequest(pkt, destIface, iface)
		debug.Log(debug.DEBUG_INFO, "Link request handling completed", "elapsed", time.Since(reqStartTime).Seconds(), "total_elapsed", time.Since(startTime).Seconds())
		return
	}

	// Otherwise, this is a data packet for an established link
	debug.Log(debug.DEBUG_INFO, "Processing link data packet", "interface", iface.GetName())

	// Parse as data packet - prepend packet type 0x00
	pkt.Raw = append([]byte{0x00}, data...)
	if err := pkt.Unpack(); err != nil {
		debug.Log(debug.DEBUG_ERROR, "Failed to unpack link data packet", "error", err, "interface", iface.GetName())
		return
	}

	// For link data packets, the destination hash is actually the link ID
	linkID := pkt.DestinationHash
	if len(linkID) > 16 {
		linkID = linkID[:16]
	}

	debug.Log(debug.DEBUG_INFO, "Link data for link ID", "link_id", fmt.Sprintf("%x", linkID), "context", fmt.Sprintf("0x%02x", pkt.Context), "packet_type", fmt.Sprintf("0x%02x", pkt.PacketType), "interface", iface.GetName())

	// Find the established link
	t.mutex.RLock()
	linkObj, exists := t.links[string(linkID)]
	t.mutex.RUnlock()

	if exists && linkObj != nil {
		debug.Log(debug.DEBUG_VERBOSE, "Routing packet to established link")
		if err := linkObj.HandleInbound(pkt); err != nil {
			debug.Log(debug.DEBUG_ERROR, "Error handling inbound packet", "error", err)
		}
	} else {
		debug.Log(debug.DEBUG_INFO, "No established link found for link ID", "link_id", fmt.Sprintf("%x", linkID))
	}
}

func (t *Transport) handleIncomingLinkRequest(pkt *packet.Packet, destIface interface{}, networkIface common.NetworkInterface) {
	startTime := time.Now()
	debug.Log(debug.DEBUG_INFO, "Handling incoming link request", "interface", networkIface.GetName())

	// The link ID is in the packet data
	linkID := pkt.Data
	if len(linkID) == 0 {
		debug.Log(debug.DEBUG_INFO, "No link ID in link request packet", "elapsed", time.Since(startTime).Seconds())
		return
	}

	debug.Log(debug.DEBUG_INFO, "Link request with ID", "id", fmt.Sprintf("%x", linkID[:8]), "full_id", fmt.Sprintf("%x", linkID), "elapsed", time.Since(startTime).Seconds())

	// Call the destination's HandleIncomingLinkRequest method
	destValue := reflect.ValueOf(destIface)
	if destValue.IsValid() && !destValue.IsNil() {
		method := destValue.MethodByName("HandleIncomingLinkRequest")
		if method.IsValid() {
			// HandleIncomingLinkRequest(pkt interface{}, transport interface{}, networkIface common.NetworkInterface) error
			args := []reflect.Value{
				reflect.ValueOf(pkt),
				reflect.ValueOf(t),
				reflect.ValueOf(networkIface),
			}
			callStartTime := time.Now()
			results := method.Call(args)
			if len(results) > 0 && !results[0].IsNil() {
				err := results[0].Interface().(error)
				debug.Log(debug.DEBUG_ERROR, "Failed to handle incoming link request", "error", err, "call_elapsed", time.Since(callStartTime).Seconds(), "total_elapsed", time.Since(startTime).Seconds())
			} else {
				debug.Log(debug.DEBUG_INFO, "Link request handled successfully by destination", "call_elapsed", time.Since(callStartTime).Seconds(), "total_elapsed", time.Since(startTime).Seconds())
			}
		} else {
			debug.Log(debug.DEBUG_ERROR, "Destination does not have HandleIncomingLinkRequest method", "elapsed", time.Since(startTime).Seconds())
		}
	} else {
		debug.Log(debug.DEBUG_ERROR, "Invalid destination object", "elapsed", time.Since(startTime).Seconds())
	}
}

func (t *Transport) handlePathResponse(data []byte, iface common.NetworkInterface) {
	if len(data) < 33 { // 32 bytes hash + 1 byte hops minimum
		return
	}

	destHash := data[:32]
	hops := data[32]
	var nextHop []byte

	if len(data) > 33 {
		nextHop = data[33:]
	}

	// Use interface name when updating path
	if iface != nil {
		t.UpdatePath(destHash, nextHop, iface.GetName(), hops)
	}
}

func (t *Transport) handleTransportPacket(data []byte, iface common.NetworkInterface) {
	if len(data) < 2 {
		return
	}

	pkt := &packet.Packet{Raw: append([]byte{0x00}, data...)}
	if err := pkt.Unpack(); err != nil {
		debug.Log(debug.DEBUG_INFO, "Failed to unpack transport packet", "error", err)
		return
	}

	headerByte := data[0]
	packetType := headerByte & 0x03
	destType := (headerByte & 0x0C) >> 2

	if packetType == packet.PacketTypeData {
		if destType == DEST_TYPE_PLAIN {
			if len(data) < 19 {
				return
			}

			context := data[18]

			if context == packet.ContextPathResponse {
				t.handlePathResponse(data[19:], iface)
				return
			}
		}

		destHash := pkt.DestinationHash
		if len(destHash) > 16 {
			destHash = destHash[:16]
		}

		debug.Log(debug.DEBUG_VERBOSE, "Looking up destination for data packet", "hash", fmt.Sprintf("%x", destHash))

		t.mutex.RLock()
		destIface, exists := t.destinations[string(destHash)]
		t.mutex.RUnlock()

		if exists {
			debug.Log(debug.DEBUG_INFO, "Routing data packet to destination", "hash", fmt.Sprintf("%x", destHash))

			destValue := reflect.ValueOf(destIface)
			if destValue.IsValid() && !destValue.IsNil() {
				method := destValue.MethodByName("Receive")
				if method.IsValid() {
					args := []reflect.Value{
						reflect.ValueOf(pkt),
						reflect.ValueOf(iface),
					}
					method.Call(args)
				} else {
					debug.Log(debug.DEBUG_VERBOSE, "Destination does not have Receive method")
				}
			}
		} else {
			debug.Log(debug.DEBUG_VERBOSE, "No destination registered for hash", "hash", fmt.Sprintf("%x", destHash))
		}
	}
}

func (t *Transport) InitializePathRequestHandler() error {
	if t.transportIdentity == nil {
		return errors.New("transport identity not initialized")
	}

	pathRequestDest, err := destination.New(t.transportIdentity, destination.IN, destination.PLAIN, "rnstransport", t, "path", "request")
	if err != nil {
		return fmt.Errorf("failed to create path request destination: %w", err)
	}

	pathRequestDest.SetPacketCallback(func(data []byte, iface common.NetworkInterface) {
		t.handlePathRequest(data, iface)
	})

	pathRequestDest.AcceptsLinks(true)
	t.pathRequestDest = pathRequestDest
	t.RegisterDestination(pathRequestDest.GetHash(), pathRequestDest)

	debug.Log(debug.DEBUG_INFO, "Path request handler initialized")
	return nil
}

func (t *Transport) handlePathRequest(data []byte, iface common.NetworkInterface) {
	if len(data) < identity.TRUNCATED_HASHLENGTH/8 {
		debug.Log(debug.DEBUG_INFO, "Path request too short")
		return
	}

	destHash := data[:identity.TRUNCATED_HASHLENGTH/8]
	var requestorTransportID []byte
	var tag []byte

	if len(data) > identity.TRUNCATED_HASHLENGTH/8*2 {
		requestorTransportID = data[identity.TRUNCATED_HASHLENGTH/8 : identity.TRUNCATED_HASHLENGTH/8*2]
		tag = data[identity.TRUNCATED_HASHLENGTH/8*2:]
		if len(tag) > identity.TRUNCATED_HASHLENGTH/8 {
			tag = tag[:identity.TRUNCATED_HASHLENGTH/8]
		}
	} else if len(data) > identity.TRUNCATED_HASHLENGTH/8 {
		tag = data[identity.TRUNCATED_HASHLENGTH/8:]
		if len(tag) > identity.TRUNCATED_HASHLENGTH/8 {
			tag = tag[:identity.TRUNCATED_HASHLENGTH/8]
		}
	}

	if tag == nil {
		debug.Log(debug.DEBUG_INFO, "Ignoring tagless path request", "dest_hash", fmt.Sprintf("%x", destHash))
		return
	}

	uniqueTag := append(destHash, tag...)
	tagStr := string(uniqueTag)

	t.mutex.Lock()
	if t.discoveryPRTags[tagStr] {
		t.mutex.Unlock()
		debug.Log(debug.DEBUG_INFO, "Ignoring duplicate path request", "dest_hash", fmt.Sprintf("%x", destHash), "tag", fmt.Sprintf("%x", tag))
		return
	}
	t.discoveryPRTags[tagStr] = true
	if len(t.discoveryPRTags) > 32000 {
		t.discoveryPRTags = make(map[string]bool)
	}
	t.mutex.Unlock()

	t.processPathRequest(destHash, iface, requestorTransportID, tag)
}

func (t *Transport) processPathRequest(destHash []byte, attachedIface common.NetworkInterface, requestorTransportID []byte, tag []byte) {
	destHashStr := string(destHash)
	debug.Log(debug.DEBUG_INFO, "Processing path request", "dest_hash", fmt.Sprintf("%x", destHash))

	t.mutex.RLock()
	localDest, isLocal := t.destinations[destHashStr]
	path, hasPath := t.paths[destHashStr]
	t.mutex.RUnlock()

	if isLocal {
		if dest, ok := localDest.(*destination.Destination); ok {
			debug.Log(debug.DEBUG_INFO, "Answering path request for local destination", "dest_hash", fmt.Sprintf("%x", destHash))
			if err := dest.Announce(true, tag, attachedIface); err != nil {
				debug.Log(debug.DEBUG_ERROR, "Failed to announce local destination for path request", "error", err)
			}
		}
		return
	}

	if hasPath {
		nextHop := path.NextHop
		if requestorTransportID != nil && string(nextHop) == string(requestorTransportID) {
			debug.Log(debug.DEBUG_INFO, "Not answering path request, next hop is requestor", "dest_hash", fmt.Sprintf("%x", destHash))
			return
		}

		debug.Log(debug.DEBUG_INFO, "Answering path request with known path", "dest_hash", fmt.Sprintf("%x", destHash), "hops", path.HopCount)

		t.mutex.RLock()
		announceEntry, hasAnnounce := t.announceTable[destHashStr]
		t.mutex.RUnlock()

		if hasAnnounce && announceEntry != nil {
			now := time.Now()
			retries := 1
			localRebroadcasts := 0
			blockRebroadcasts := true
			announceHops := path.HopCount

			retransmitTimeout := now.Add(time.Duration(400) * time.Millisecond)

			entry := &PathAnnounceEntry{
				CreatedAt:         now,
				RetransmitTimeout: retransmitTimeout,
				Retries:           retries,
				ReceivedFrom:      path.Interface,
				AnnounceHops:      announceHops,
				Packet:            announceEntry.Packet,
				LocalRebroadcasts: localRebroadcasts,
				BlockRebroadcasts: blockRebroadcasts,
				AttachedInterface: attachedIface,
			}

			t.mutex.Lock()
			if _, held := t.announceTable[destHashStr]; held {
				t.heldAnnounces[destHashStr] = t.announceTable[destHashStr]
			}
			t.announceTable[destHashStr] = entry
			t.mutex.Unlock()
		}
		return
	}

	if attachedIface != nil {
		debug.Log(debug.DEBUG_INFO, "Attempting to discover unknown path", "dest_hash", fmt.Sprintf("%x", destHash))

		t.mutex.Lock()
		if _, exists := t.discoveryPathRequests[destHashStr]; exists {
			t.mutex.Unlock()
			debug.Log(debug.DEBUG_INFO, "Path request already pending", "dest_hash", fmt.Sprintf("%x", destHash))
			return
		}

		prEntry := &DiscoveryPathRequest{
			DestinationHash: destHash,
			Timeout:         time.Now().Add(15 * time.Second),
			RequestingIface: attachedIface,
		}
		t.discoveryPathRequests[destHashStr] = prEntry
		t.mutex.Unlock()

		for name, iface := range t.interfaces {
			if iface != attachedIface && iface.IsEnabled() {
				req := &PathRequest{
					DestinationHash: destHash,
					Tag:             tag,
					TTL:             15,
					Recursive:       true,
				}
				if err := t.sendPathRequest(req, name); err != nil {
					debug.Log(debug.DEBUG_INFO, "Failed to send path request", "interface", name, "error", err)
				}
			}
		}
	} else {
		debug.Log(debug.DEBUG_INFO, "Ignoring path request, no path known", "dest_hash", fmt.Sprintf("%x", destHash))
	}
}

func (t *Transport) SendPacket(p *packet.Packet) error {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	debug.Log(debug.DEBUG_VERBOSE, "Sending packet", "type", fmt.Sprintf("0x%02x", p.PacketType), "header", p.HeaderType)

	data, err := p.Serialize()
	if err != nil {
		debug.Log(debug.DEBUG_INFO, "Packet serialization failed", "error", err)
		return fmt.Errorf("failed to serialize packet: %w", err)
	}
	debug.Log(debug.DEBUG_TRACE, "Serialized packet size", "bytes", len(data))

	// Use the DestinationHash field directly for path lookup
	destHash := p.DestinationHash
	if len(destHash) > 16 {
		destHash = destHash[:16]
	}
	debug.Log(debug.DEBUG_PACKETS, "Destination hash", "hash", fmt.Sprintf("%x", destHash))

	path, exists := t.paths[string(destHash)]
	if !exists {
		debug.Log(debug.DEBUG_INFO, "No path found for destination", "hash", fmt.Sprintf("%x", destHash))
		return errors.New("no path to destination")
	}

	debug.Log(debug.DEBUG_TRACE, "Using path", "interface", path.Interface.GetName(), "nextHop", fmt.Sprintf("%x", path.NextHop), "hops", path.HopCount)

	if err := path.Interface.Send(data, ""); err != nil {
		debug.Log(debug.DEBUG_INFO, "Failed to send packet", "error", err)
		return fmt.Errorf("failed to send packet: %w", err)
	}

	p.Sent = true
	p.SentAt = time.Now()

	if p.CreateReceipt {
		receipt := packet.NewPacketReceipt(p)
		t.RegisterReceipt(receipt)
		debug.Log(debug.DEBUG_PACKETS, "Created packet receipt")
	}

	debug.Log(debug.DEBUG_ALL, "Packet sent successfully")
	return nil
}

func (t *Transport) RegisterLink(linkID []byte, linkObj LinkInterface) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if len(linkID) > 16 {
		linkID = linkID[:16]
	}

	t.links[string(linkID)] = linkObj
	debug.Log(debug.DEBUG_VERBOSE, "Registered link", "link_id", fmt.Sprintf("%x", linkID))
}

func (t *Transport) UnregisterLink(linkID []byte) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if len(linkID) > 16 {
		linkID = linkID[:16]
	}
	delete(t.links, string(linkID))
	debug.Log(debug.DEBUG_VERBOSE, "Unregistered link", "link_id", fmt.Sprintf("%x", linkID))
}

func (l *Link) OnConnected(cb func()) {
	l.connectedCb = cb
	if !l.establishedAt.IsZero() && cb != nil {
		cb()
	}
}

func (l *Link) OnDisconnected(cb func()) {
	l.disconnectedCb = cb
}

func (l *Link) GetRemoteIdentity() []byte {
	return l.remoteIdentity
}

func (l *Link) TrackPhyStats(track bool) {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	l.physicalStats = track
}

func (l *Link) GetRSSI() int {
	// Implement physical layer stats
	return 0
}

func (l *Link) GetSNR() float64 {
	// Implement physical layer stats
	return 0
}

func (l *Link) GetQ() float64 {
	// Implement physical layer stats
	return 0
}

func (l *Link) SetResourceStrategy(strategy int) error {
	l.mutex.Lock()
	defer l.mutex.Unlock()

	if strategy != AcceptNone && strategy != AcceptAll && strategy != AcceptApp {
		return errors.New("invalid resource strategy")
	}

	l.resourceStrategy = strategy
	return nil
}

func (l *Link) SetResourceStartedCallback(cb func(interface{})) {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	l.resourceStartedCb = cb
}

func (l *Link) SetResourceConcludedCallback(cb func(interface{})) {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	l.resourceConcludedCb = cb
}

func (l *Link) SetRemoteIdentifiedCallback(cb func(*Link, []byte)) {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	l.remoteIdentifiedCb = cb
}

func (l *Link) HandleResource(resource interface{}) bool {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	switch l.resourceStrategy {
	case AcceptNone:
		return false
	case AcceptAll:
		return true
	case AcceptApp:
		if l.resourceCb != nil {
			return l.resourceCb(resource)
		}
		return false
	default:
		return false
	}
}

func (t *Transport) Start() error {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	return nil
}

// LinkInterface defines the methods required by Channel
type LinkInterface interface {
	GetStatus() byte
	GetRTT() float64
	RTT() float64
	GetLinkID() []byte
	Send(data []byte) interface{}
	Resend(packet interface{}) error
	SetPacketTimeout(packet interface{}, callback func(interface{}), timeout time.Duration)
	SetPacketDelivered(packet interface{}, callback func(interface{}))
	HandleInbound(pkt *packet.Packet) error
	ValidateLinkProof(pkt *packet.Packet, networkIface common.NetworkInterface) error
}

func (l *Link) GetRTT() float64 {
	l.mutex.RLock()
	defer l.mutex.RUnlock()
	return l.rtt.Seconds()
}

func (l *Link) RTT() float64 {
	return l.GetRTT()
}

func (l *Link) Resend(p interface{}) error {
	if pkt, ok := p.(*packet.Packet); ok {
		t := GetTransportInstance()
		if t == nil {
			return fmt.Errorf("transport not initialized")
		}
		return t.SendPacket(pkt)
	}
	return fmt.Errorf("invalid packet type")
}

func (l *Link) SetPacketTimeout(p interface{}, callback func(interface{}), timeout time.Duration) {
	if pkt, ok := p.(*packet.Packet); ok {
		time.AfterFunc(timeout, func() {
			callback(pkt)
		})
	}
}

func (l *Link) SetPacketDelivered(p interface{}, callback func(interface{})) {
	if pkt, ok := p.(*packet.Packet); ok {
		l.mutex.Lock()
		l.rtt = time.Since(time.Now())
		l.mutex.Unlock()
		callback(pkt)
	}
}

func (l *Link) GetStatus() int {
	l.mutex.RLock()
	defer l.mutex.RUnlock()
	return l.status
}

func CreateAnnouncePacket(destHash []byte, identity *identity.Identity, appData []byte, destName string, hops byte, config *common.ReticulumConfig) []byte {
	debug.Log(debug.DEBUG_INFO, "Creating announce packet", "destName", destName)
	debug.Log(debug.DEBUG_INFO, "Input", "destHash", fmt.Sprintf("%x", destHash[:8]), "appData", string(appData), "hops", hops)

	// Create header (2 bytes)
	headerByte := byte(
		(0 << 7) | // Interface flag (IFAC_NONE)
			(0 << 6) | // Header type (HEADER_TYPE_1)
			(0 << 5) | // Context flag
			(0 << 4) | // Propagation type (BROADCAST)
			(0 << 2) | // Destination type (SINGLE)
			PACKET_TYPE_ANNOUNCE, // Packet type (0x01)
	)

	debug.Log(debug.DEBUG_ALL, "Created header byte", "header", fmt.Sprintf("0x%02x", headerByte), "hops", hops)
	packet := []byte{headerByte, hops}
	debug.Log(debug.DEBUG_ALL, "Initial packet size", "bytes", len(packet))

	// Add destination hash (16 bytes)
	if len(destHash) > 16 {
		destHash = destHash[:16]
	}
	debug.Log(debug.DEBUG_ALL, "Adding destination hash (16 bytes)", "hash", fmt.Sprintf("%x", destHash))
	packet = append(packet, destHash...)
	debug.Log(debug.DEBUG_ALL, "Packet size after adding destination hash", "bytes", len(packet))

	// Get full public key and split into encryption and signing keys
	pubKey := identity.GetPublicKey()
	encKey := pubKey[:32]  // x25519 public key for encryption
	signKey := pubKey[32:] // Ed25519 public key for signing
	debug.Log(debug.DEBUG_ALL, "Full public key", "key", fmt.Sprintf("%x", pubKey))

	// Add encryption key (32 bytes)
	debug.Log(debug.DEBUG_ALL, "Adding encryption key (32 bytes)", "key", fmt.Sprintf("%x", encKey))
	packet = append(packet, encKey...)
	debug.Log(debug.DEBUG_ALL, "Packet size after adding encryption key", "bytes", len(packet))

	// Add signing key (32 bytes)
	debug.Log(debug.DEBUG_ALL, "Adding signing key (32 bytes)", "key", fmt.Sprintf("%x", signKey))
	packet = append(packet, signKey...)
	debug.Log(debug.DEBUG_ALL, "Packet size after adding signing key", "bytes", len(packet))

	// Add name hash (10 bytes)
	nameHash := sha256.Sum256([]byte(destName))
	debug.Log(debug.DEBUG_ALL, "Adding name hash (10 bytes)", "destName", destName, "hash", fmt.Sprintf("%x", nameHash[:10]))
	packet = append(packet, nameHash[:10]...)
	debug.Log(debug.DEBUG_ALL, "Packet size after adding name hash", "bytes", len(packet))

	// Add random hash (10 bytes)
	randomBytes := make([]byte, 5)
	_, err := rand.Read(randomBytes) // #nosec G104
	if err != nil {
		debug.Log(debug.DEBUG_ALL, "Failed to read random bytes", "error", err)
		return nil // Or handle the error appropriately
	}
	timeBytes := make([]byte, 8)
	binary.BigEndian.PutUint64(timeBytes, uint64(time.Now().Unix())) // #nosec G115
	debug.Log(debug.DEBUG_ALL, "Adding random hash (10 bytes)", "random", fmt.Sprintf("%x", randomBytes), "time", fmt.Sprintf("%x", timeBytes[:5]))
	packet = append(packet, randomBytes...)
	packet = append(packet, timeBytes[:5]...)
	debug.Log(debug.DEBUG_ALL, "Packet size after adding random hash", "bytes", len(packet))

	// Create msgpack array for app data
	nameBytes := []byte(destName)
	appDataMsg := []byte{0x92} // array of 2 elements

	// Add name as first element
	appDataMsg = append(appDataMsg, 0xc4, byte(len(nameBytes)))
	appDataMsg = append(appDataMsg, nameBytes...)

	// Add app data as second element
	appDataMsg = append(appDataMsg, 0xc4, byte(len(appData)))
	appDataMsg = append(appDataMsg, appData...)

	// Create signature over destination hash and app data
	signData := append(destHash, appDataMsg...)
	signature := identity.Sign(signData)
	debug.Log(debug.DEBUG_ALL, "Adding signature (64 bytes)", "signature", fmt.Sprintf("%x", signature))
	packet = append(packet, signature...)
	debug.Log(debug.DEBUG_ALL, "Packet size after adding signature", "bytes", len(packet))

	// Finally add the app data message
	packet = append(packet, appDataMsg...)
	debug.Log(debug.DEBUG_INFO, "Final packet size", "bytes", len(packet))
	debug.Log(debug.DEBUG_INFO, "appDataMsg", "data", fmt.Sprintf("%x", appDataMsg), "len", len(appDataMsg))

	return packet
}

func (t *Transport) GetInterfaces() map[string]common.NetworkInterface {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	interfaces := make(map[string]common.NetworkInterface, len(t.interfaces))
	for k, v := range t.interfaces {
		interfaces[k] = v
	}

	return interfaces
}

func (t *Transport) GetConfig() *common.ReticulumConfig {
	return t.config
}

func (t *Transport) RegisterReceipt(receipt *packet.PacketReceipt) {
	t.receiptsMutex.Lock()
	defer t.receiptsMutex.Unlock()
	t.receipts = append(t.receipts, receipt)
	debug.Log(debug.DEBUG_PACKETS, "Registered packet receipt", "hash", fmt.Sprintf("%x", receipt.GetHash()[:8]))
}

func (t *Transport) UnregisterReceipt(receipt *packet.PacketReceipt) {
	t.receiptsMutex.Lock()
	defer t.receiptsMutex.Unlock()

	for i, r := range t.receipts {
		if r == receipt {
			t.receipts = append(t.receipts[:i], t.receipts[i+1:]...)
			debug.Log(debug.DEBUG_PACKETS, "Unregistered packet receipt")
			return
		}
	}
}

func (t *Transport) handleProofPacket(pkt *packet.Packet, iface common.NetworkInterface) {
	debug.Log(debug.DEBUG_PACKETS, "Processing proof packet", "size", len(pkt.Data), "context", fmt.Sprintf("0x%02x", pkt.Context))

	if pkt.Context == packet.ContextLRProof {
		linkID := pkt.DestinationHash
		if len(linkID) > 16 {
			linkID = linkID[:16]
		}

		debug.Log(debug.DEBUG_INFO, "Received link proof packet", "link_id", fmt.Sprintf("%x", linkID), "data_len", len(pkt.Data))

		t.mutex.RLock()
		link, exists := t.links[string(linkID)]
		t.mutex.RUnlock()

		if exists && link != nil {
			debug.Log(debug.DEBUG_INFO, "Found link for proof, validating", "link_id", fmt.Sprintf("%x", linkID), "interface", iface.GetName())
			startTime := time.Now()
			if err := link.ValidateLinkProof(pkt, iface); err != nil {
				debug.Log(debug.DEBUG_ERROR, "Link proof validation failed", "error", err, "link_id", fmt.Sprintf("%x", linkID), "elapsed", time.Since(startTime).Seconds())
			} else {
				debug.Log(debug.DEBUG_INFO, "Link proof validated successfully", "link_id", fmt.Sprintf("%x", linkID), "elapsed", time.Since(startTime).Seconds())
			}
			return
		}
		debug.Log(debug.DEBUG_INFO, "No link found for proof packet", "link_id", fmt.Sprintf("%x", linkID))
		return
	}

	var proofHash []byte
	if len(pkt.Data) == packet.EXPL_LENGTH {
		proofHash = pkt.Data[:identity.HASHLENGTH/8]
		debug.Log(debug.DEBUG_PACKETS, "Explicit proof", "hash", fmt.Sprintf("%x", proofHash[:8]))
	} else {
		debug.Log(debug.DEBUG_PACKETS, "Implicit proof")
	}

	t.receiptsMutex.RLock()
	receipts := make([]*packet.PacketReceipt, len(t.receipts))
	copy(receipts, t.receipts)
	t.receiptsMutex.RUnlock()

	for _, receipt := range receipts {
		receiptValidated := false

		if proofHash != nil {
			receiptHash := receipt.GetHash()
			if string(receiptHash) == string(proofHash) {
				receiptValidated = receipt.ValidateProofPacket(pkt)
			}
		} else {
			receiptValidated = receipt.ValidateProofPacket(pkt)
		}

		if receiptValidated {
			debug.Log(debug.DEBUG_PACKETS, "Proof validated for receipt")
			t.UnregisterReceipt(receipt)
			return
		}
	}

	debug.Log(debug.DEBUG_PACKETS, "No matching receipt for proof")
}
