#ifndef NETWORK_LINK_H
#define NETWORK_LINK_H

#include <cstdint>
#include <optional>
#include <functional>

#include "core/time.h"
#include "core/types.h"
#include "core/node.h"
#include "core/simulator.h"
#include "core/error.h"
#include "network/packet.h"
#include "network/network_node.h"

namespace dofs {

// Link represents a full-duplex point-to-point link between two NetworkNodes.
// It exposes a reservation-based timing API; the switch owns scheduling.
//
// API for switch-owned scheduling:
//   - next_available(sender): when this direction can start sending next
//   - serialization_time(bytes): how long to serialize 'bytes' at current bw
//   - propagation_latency(): current one-way latency
//   - reserve(bytes, sender): returns {start, finish} and advances cursor
//   - send_pkt(pkt, caller): uses reserve() + schedules delivery itself.
class Link {
public:
        struct Reservation {
                Time start;   // serialization start time on the link
                Time finish;  // serialization finish time on the link
        };

        Link(Simulator *const sim,
             LinkId id,
             NetworkNode *a, PortId a_port,
             NetworkNode *b, PortId b_port,
             Time latency,
             double bandwidth_gbps) noexcept;

        void send_pkt(Packet &pkt, NodeId caller);

        void schedule_delivery_after(Packet &pkt, NodeId caller, Time after);

        Time next_available(NodeId sender) const noexcept;

        std::optional<Reservation> reserve(Bytes bytes, NodeId sender) noexcept;

        Time serialization_time(Bytes bytes) const noexcept {
                return serialization_time(bytes, _bandwidth_gbps_cur);
        }

        Time propagation_latency() const noexcept {
                return _latency_cur;
        }

        LinkId id() const noexcept {
                return _id;
        }
        LinkStatus status() const noexcept {
                return _status;
        }
        double bandwidth_gbps() const noexcept {
                return _bandwidth_gbps_cur;
        }

        // Endpoint accessors (A/B as constructed)
        NodeId src_id() const noexcept {
                return _a_id;        // 'A' end
        }
        NodeId dst_id() const noexcept {
                return _b_id;        // 'B' end
        }
        PortId src_port() const noexcept {
                return _a_port;
        }
        PortId dst_port() const noexcept {
                return _b_port;
        }

        // Status changes:
        // - set_status(OK): restore initial latency/bandwidth
        // - set_status(DOWN): link becomes inactive (reservations/sends rejected)
        // - set_status(THROTTLING, new_latency, new_bw_gbps): degrade
        //   (reject if new_latency < initial or new_bw_gbps > initial)
        void set_status(LinkStatus s,
                        Time new_latency = Time(0),
                        double new_bandwidth_gbps = 0.0);

        static Time serialization_time(Bytes bytes, double gbps) noexcept;

private:
        enum class Dir : uint8_t { AtoB, BtoA };

        std::optional<Dir> _dir_from(NodeId sender) const noexcept;

        Simulator *const _sim;

        LinkId _id;

        NetworkNode *_a;
        NetworkNode *_b;
        NodeId _a_id;
        NodeId _b_id;
        PortId _a_port;
        PortId _b_port;

        Time _latency_init;
        double _bandwidth_gbps_init;
        Time _latency_cur;
        double _bandwidth_gbps_cur;
        LinkStatus _status;

        Time _next_available_ab;
        Time _next_available_ba;
};

} // namespace dofs

#endif // NETWORK_LINK_H
