use crate::CodecLayer;
use crate::SlotId;
use crate::session::Egress;
use dambi::Bytes;

/// Client-side protocol.
///
/// One trait owns parsing AND handling — there is no separate `Framer` trait
/// or `Framer` accessor. Per-connection parsing state lives in [`ConnState`];
/// shared protocol config lives in `&self`. Reusable parsers are expressed as
/// free functions or helper structs that `parse` delegates to.
///
/// [`ConnState`]: ClientProtocol::ConnState
pub trait ClientProtocol: 'static {
    type Head;
    type ConnState: Default + 'static;
    type CodecLayer: CodecLayer;

    /// Try to parse one frame from the connection's ingress buffer.
    ///
    /// Return `Some((head, consumed))` to advance past `consumed` bytes and
    /// fire [`on_response`](ClientProtocol::on_response). Return `None` to
    /// wait for more bytes (no advance).
    ///
    /// `state` is mutable so a stateful parser can advance internal phases
    /// (STARTTLS reply → length-prefixed messages, HTTP/1 → /2 upgrade, ...).
    fn parse(&self, state: &mut Self::ConnState, buf: &Bytes) -> Option<(Self::Head, usize)>;

    fn on_connect(
        &mut self,
        conn_id: SlotId,
        conn_state: &mut Self::ConnState,
        out: Egress<'_, Self::CodecLayer>,
    ) {
        let _ = (conn_id, conn_state, out);
    }

    fn on_response(
        &mut self,
        conn_id: SlotId,
        conn_state: &mut Self::ConnState,
        head: Self::Head,
        out: Egress<'_, Self::CodecLayer>,
    );

    fn wants_close(&self, conn_state: &Self::ConnState) -> bool {
        let _ = conn_state;
        false
    }

    fn on_disconnect(
        &mut self,
        conn_id: SlotId,
        conn_state: &mut Self::ConnState,
        out: Egress<'_, Self::CodecLayer>,
    ) {
        let _ = (conn_id, conn_state, out);
    }
}
