Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

use std::future::Future;

use futures::Stream;

/// A handler for accessing the headers of the current HTTP Flow.
pub trait HeadersHandler {
    /// Returns a list of headers represented by pairs of name and value.
    fn headers(&self) -> Vec<(String, String)>;

    /// Returns a header by `name` if it exists.
    fn header(&self, name: &str) -> Option<String>;

    /// Adds a header `value` by `name`.
    /// If there is a header with the same `name`, this method will keep that and adding another
    /// header entry with the same `name` and the new `value`.
    fn add_header(&self, name: &str, value: &str);

    /// Sets a header `value` by `name`.
    /// If there is a header with the same `name`, this method will replace the header `value`.
    fn set_header(&self, name: &str, value: &str);

    /// Sets a list of headers represented by pairs of name and value.
    fn set_headers(&self, headers: Vec<(&str, &str)>);

    /// Removes a header by `name` if it exists.
    fn remove_header(&self, name: &str);
}

/// The error for [`BodyHandler`] operations.
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
pub enum BodyError {
    /// The current HTTP Flow does not have a body.
    #[error("Body not sent")]
    BodyNotSent,

    /// The body size exceeds what the low-level host allows.
    #[error("Exceeded body size: {0}")]
    ExceededBodySize(usize),
}

/// A handler for accessing the body of the current HTTP Flow.
pub trait BodyHandler {
    /// Returns the body of the current HTTP Flow.
    fn body(&self) -> Vec<u8>;

    /// Sets the `body` of the current HTTP Flow in binary format.
    ///
    /// # Errors
    ///
    /// This method will return an error in the following situations, but is not
    /// limited to just these cases:
    ///
    /// * The current HTTP Flow does not have a body and it can not be injected.
    /// * The `body` size exceeds what the low-level host allows.
    fn set_body(&self, body: &[u8]) -> Result<(), BodyError>;
}

/// A handler for accessing the headers and body of the current HTTP Flow.
pub trait HeadersBodyHandler: HeadersHandler + BodyHandler {}

/// Abstract state of the HTTP Flow state-machine allowed to be moved into the Body state.
pub trait IntoBodyState {
    /// Concrete type for Body state of the Flow state-machine.
    type BodyState: BodyState;

    /// A [`Future`] that will be completed when the Body state is reached.
    type BodyStateFuture: Future<Output = Self::BodyState>;

    /// Moves the current state into the Body state of the HTTP Flow state-machine.
    /// The [`Future`] returned by this method can be cancelled.
    #[must_use]
    fn into_body_state(self) -> Self::BodyStateFuture;
}

pub trait IntoBodyStreamState {
    type BodyStreamState: BodyStreamState;
    type BodyStreamStateFuture: Future<Output = Self::BodyStreamState>;

    #[must_use]
    fn into_body_stream_state(self) -> Self::BodyStreamStateFuture;
}

#[cfg(feature = "enable_stop_iteration")]
/// Abstract state of the HTTP Flow state-machine allowed to be moved into the HeadersBody state.
pub trait IntoHeadersBodyState {
    /// Concrete type for HeadersBody state of the Flow state-machine.
    type HeadersBodyState: HeadersBodyState;

    /// A [`Future`] that will be completed when the HeadersBody state is reached.
    type HeadersBodyStateFuture: Future<Output = Self::HeadersBodyState>;

    /// Moves the current state into the HeadersBody state of the HTTP Flow state-machine.
    /// The [`Future`] returned by this method can be cancelled.
    #[must_use]
    fn into_headers_body_state(self) -> Self::HeadersBodyStateFuture;
}

#[cfg(feature = "enable_stop_iteration")]
/// Abstract initial state of the HTTP Flow state-machine.
pub trait EntityState: IntoBodyState + IntoBodyStreamState + IntoHeadersBodyState {
    /// Concrete type for Headers state of the Flow state-machine.
    type HeadersState: HeadersState;

    /// A [`Future`] that will be completed when the Headers state is reached.
    type HeadersStateFuture: Future<Output = Self::HeadersState>;

    /// Moves the current initial state into the Headers state of the HTTP Flow state-machine.
    /// The [`Future`] returned by this method can be cancelled.
    #[must_use]
    fn into_headers_state(self) -> Self::HeadersStateFuture;
}

#[cfg(feature = "enable_stop_iteration")]
/// Abstract Headers state of the HTTP Flow state-machine.
pub trait HeadersState: IntoBodyState + IntoBodyStreamState + IntoHeadersBodyState {
    /// Returns a handler for accessing the headers of the current HTTP Flow.
    fn handler(&self) -> &dyn HeadersHandler;

    /// Returns [`true`] if the current HTTP Flow contains body.
    fn contains_body(&self) -> bool;
}

#[cfg(not(feature = "enable_stop_iteration"))]
/// Abstract initial state of the HTTP Flow state-machine.
pub trait EntityState: IntoBodyState + IntoBodyStreamState {
    /// Concrete type for Headers state of the Flow state-machine.
    type HeadersState: HeadersState;

    /// A [`Future`] that will be completed when the Headers state is reached.
    type HeadersStateFuture: Future<Output = Self::HeadersState>;

    /// Moves the current initial state into the Headers state of the HTTP Flow state-machine.
    /// The [`Future`] returned by this method can be cancelled.
    #[must_use]
    fn into_headers_state(self) -> Self::HeadersStateFuture;
}

#[cfg(not(feature = "enable_stop_iteration"))]
/// Abstract Headers state of the HTTP Flow state-machine.
pub trait HeadersState: IntoBodyState + IntoBodyStreamState {
    /// Returns a handler for accessing the headers of the current HTTP Flow.
    fn handler(&self) -> &dyn HeadersHandler;

    /// Returns [`true`] if the current HTTP Flow contains body.
    fn contains_body(&self) -> bool;
}

/// Abstract Body state of the HTTP Flow state-machine.
pub trait BodyState {
    /// Returns a handler for accessing the body of the current HTTP Flow.
    fn handler(&self) -> &dyn BodyHandler;

    /// Returns [`true`] if the current HTTP Flow contains body.
    fn contains_body(&self) -> bool;
}

/// Abstract HeadersBody state of the HTTP Flow state-machine.
pub trait HeadersBodyState {
    /// Returns a handler for accessing the headers and body of the current HTTP Flow.
    fn handler(&self) -> &dyn HeadersBodyHandler;

    /// Returns [`true`] if the current HTTP Flow contains body.
    fn contains_body(&self) -> bool;
}

pub struct Chunk {
    bytes: Vec<u8>,
}

impl Chunk {
    #[cfg(feature = "experimental")]
    pub fn new(bytes: Vec<u8>) -> Self {
        Self { bytes }
    }

    #[cfg(not(feature = "experimental"))]
    pub(crate) fn new(bytes: Vec<u8>) -> Self {
        Self { bytes }
    }

    pub fn bytes(&self) -> &[u8] {
        &self.bytes
    }

    pub fn into_bytes(self) -> Vec<u8> {
        self.bytes
    }
}

pub trait BodyStreamState {
    type Stream<'b>: Stream<Item = Chunk>
    where
        Self: 'b;

    fn contains_body(&self) -> bool;

    fn stream(&self) -> Self::Stream<'_>;

    #[cfg(feature = "experimental")]
    fn write_chunk(&self, chunk: Chunk) -> Result<(), BodyError>;
}