use std::future::Future;
use std::pin::Pin;

use crate::error::Error;
use crate::state::StateMap;

/// Metadata for a single handler parameter.
///
/// Generated by the `#[handler]` proc macro. Describes the parameter name,
/// Rust type, the extractor kind (`"State"`, `"Data"`, `"Custom"`,
/// `"Receiver"`, or `"Sender"`), and an optional [`Structure`] function
/// pointer for recursive type discovery during code generation.
#[derive(Debug, Clone, Copy)]
pub struct ParamMeta {
    /// Parameter name as declared in the handler function signature.
    pub name: &'static str,
    /// Rust type as a string (e.g. `"String"`, `"i64"`).
    pub ty: &'static str,
    /// Extractor kind: `"State"`, `"Data"`, `"Custom"`, `"Receiver"`, or `"Sender"`.
    pub extractor: &'static str,
    /// Function pointer returning [`TagMeta`] for complex types (structs or enums).
    /// Used by code generators to recursively discover nested type definitions.
    pub structure: Option<fn() -> &'static TagMeta>,
}

/// Metadata for a registered handler function.
///
/// Generated by the `#[handler]` proc macro. Contains the handler's name,
/// description, parameters, return type, and connection mode.
#[derive(Debug, Clone, Copy)]
pub struct HandlerMeta {
    /// Rust function name (e.g. `"list_users"`).
    pub name: &'static str,
    /// Human-readable description from `#[handler(desc("..."))]`.
    pub desc: &'static str,
    /// Optional API name override from `#[handler(name("..."))]`, used as the
    /// client-side method name. An empty string falls back to `name`.
    pub api_name: &'static str,
    /// Parameter metadata, one entry per handler parameter.
    pub params: &'static [ParamMeta],
    /// Return type as a string (e.g. `"UserList"`, `"()"`).
    pub return_type: &'static str,
    /// Function pointer returning [`TagMeta`] for the return type's structure.
    pub return_structure: Option<fn() -> &'static TagMeta>,
    /// Whether this handler uses [`Receiver`]/[`Sender`] for persistent connections.
    pub long_connection: bool,
    /// Whether this is an ordinary HTTP handler (uses Query/Param/Body/Header extractors).
    pub is_ordinary: bool,
    /// HTTP method for ordinary handlers (`"GET"`, `"POST"`, etc.). Empty for binary handlers.
    pub method: &'static str,
    /// Route path for ordinary handlers (e.g. `"/users/:id"`). Empty for binary handlers.
    pub path: &'static str,
    /// Client-side cache duration in seconds. 0 means no caching.
    pub cache_seconds: u64,
}

/// Trait for type-erased handler invocation.
///
/// Implemented automatically by the `#[handler]` proc macro. Used internally
/// by the framework to dispatch binary-protocol requests to handlers.
pub trait HandlerInvoker: Send + Sync {
    /// Invoke the handler with the given shared state and binary payload.
    ///
    /// Returns the serialized response bytes on success, or an [`Error`] on failure.
    fn call(
        &self,
        state: &StateMap,
        payload: &[u8],
    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, Error>> + Send + '_>>;

    /// Returns `true` if this handler uses persistent connections (WebSocket/TCP).
    fn is_long_connection(&self) -> bool {
        false
    }

    /// Spawn a persistent handler and return its `conn_id` immediately.
    ///
    /// `server_rx` receives bytes from the client; `server_tx` sends bytes to
    /// the client. The default implementation delegates to [`call`](HandlerInvoker::call)
    /// and ignores the channel pair.
    fn call_stream<'a>(
        &'a self,
        state: &'a StateMap,
        payload: &'a [u8],
        server_tx: tokio::sync::mpsc::Sender<Vec<u8>>,
        server_rx: tokio::sync::mpsc::Receiver<Vec<u8>>,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, Error>> + Send + 'a>> {
        let _ = (server_tx, server_rx);
        Box::pin(async move { self.call(state, payload).await })
    }
}

/// Trait for type-erased ordinary HTTP handler invocation.
///
/// Used by the ordinary HTTP server to dispatch standard HTTP requests
/// (with query parameters, path segments, headers, and body) to handlers.
#[cfg(feature = "ordinary-http")]
pub trait OrdinaryHandlerInvoker: Send + Sync {
    /// Invoke the ordinary handler with the full HTTP request, extracted path
    /// parameters, the raw query string, and shared application state.
    fn call_ordinary(
        &self,
        req: hyper::Request<hyper::body::Incoming>,
        path_params: &std::collections::HashMap<String, String>,
        query_string: &str,
        state: &StateMap,
    ) -> Pin<
        Box<
            dyn Future<
                    Output = Result<
                        hyper::Response<http_body_util::Full<hyper::body::Bytes>>,
                        Error,
                    >,
                > + Send
                + '_,
        >,
    >;
}

/// A compiled handler reference, produced by the `register!` macro.
///
/// Contains the handler's static name, invoker trait object, metadata, and
/// optionally an ordinary HTTP invoker (when the handler was defined with
/// `#[get]`/`#[post]`/etc.).
#[derive(Clone)]
pub struct HandlerEntry {
    /// Handler function name.
    pub name: &'static str,
    /// Static reference to the type-erased binary invoker.
    pub invoker: &'static dyn HandlerInvoker,
    /// Handler metadata for code generation and introspection.
    pub meta: &'static HandlerMeta,
    /// Ordinary HTTP invoker, set only for handlers defined with
    /// `#[get]`/`#[post]`/`#[put]`/`#[delete]`/`#[patch]`.
    #[cfg(feature = "ordinary-http")]
    pub ordinary_invoker: Option<&'static dyn OrdinaryHandlerInvoker>,
}

impl HandlerEntry {
    /// Creates a `HandlerEntry` for a binary-protocol handler.
    ///
    /// The `ordinary_invoker` field (when present) is set to `None`.
    /// This constructor is the stable public API; proc macros emit a call
    /// to it so they don't need to know about optional fields.
    #[doc(hidden)]
    pub fn new(
        name: &'static str,
        invoker: &'static dyn HandlerInvoker,
        meta: &'static HandlerMeta,
    ) -> Self {
        Self {
            name,
            invoker,
            meta,
            #[cfg(feature = "ordinary-http")]
            ordinary_invoker: None,
        }
    }

    /// Creates a `HandlerEntry` with an ordinary HTTP invoker.
    ///
    /// Only available with the `ordinary-http` feature. Used by proc macros
    /// for `#[get]`/`#[post]`/etc. handlers.
    #[doc(hidden)]
    #[cfg(feature = "ordinary-http")]
    pub fn with_ordinary(
        name: &'static str,
        invoker: &'static dyn HandlerInvoker,
        meta: &'static HandlerMeta,
        ordinary_invoker: &'static dyn OrdinaryHandlerInvoker,
    ) -> Self {
        Self {
            name,
            invoker,
            meta,
            ordinary_invoker: Some(ordinary_invoker),
        }
    }
}

/// A node in the handler tree.
///
/// Handlers form a tree structure: leaf nodes represent actual handler
/// functions, while group nodes (`Handler::group`) act as namespaces.
/// The `offset` field is a global index used for binary protocol dispatch.
#[derive(Clone)]
pub struct Handler {
    /// Handler or group name (one segment of the API path).
    pub name: &'static str,
    /// Invoker for leaf nodes; [`DummyInvoker`] for group nodes.
    pub invoker: &'static dyn HandlerInvoker,
    /// Metadata; dummy metadata for group nodes.
    pub meta: &'static HandlerMeta,
    /// Child handlers or groups (empty for leaf nodes).
    pub children: Vec<Handler>,
    /// Global offset index for binary protocol dispatch.
    pub offset: usize,
    /// HTTP method for ordinary routes. Empty for binary and group nodes.
    pub method: &'static str,
    /// Route path for ordinary routes. Empty for binary and group nodes.
    pub path: &'static str,
    /// Ordinary handler entry for group-nested ordinary routes.
    #[cfg(feature = "ordinary-http")]
    pub ordinary_def: Option<HandlerEntry>,
}

// Static dummy metadata shared by all group (namespace) nodes.
static DUMMY_META: HandlerMeta = HandlerMeta {
    name: "",
    desc: "",
    api_name: "",
    params: &[],
    return_type: "()",
    return_structure: None,
    long_connection: false,
    is_ordinary: false,
    method: "",
    path: "",
    cache_seconds: 0,
};

/// Placeholder invoker for group (namespace) nodes.
///
/// Group nodes are organizational containers and should never be invoked
/// directly. Calling this invoker returns an error indicating a misdirected
/// request.
pub struct DummyInvoker;

impl HandlerInvoker for DummyInvoker {
    fn call(
        &self,
        _state: &StateMap,
        _payload: &[u8],
    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, Error>> + Send + '_>> {
        Box::pin(async {
            Err(Error::StateNotFound {
                message: "group handler".into(),
            })
        })
    }
}

impl Handler {
    /// Creates a [`Handler`] from a [`HandlerEntry`].
    #[doc(hidden)]
    pub fn from_entry(entry: HandlerEntry) -> Self {
        Self {
            name: entry.name,
            invoker: entry.invoker,
            meta: entry.meta,
            children: Vec::new(),
            offset: 0,
            method: "",
            path: "",
            #[cfg(feature = "ordinary-http")]
            ordinary_def: None,
        }
    }

    /// Creates a new group (namespace) node with the given name.
    #[doc(hidden)]
    pub fn group(name: &'static str) -> Self {
        Self {
            name,
            invoker: &DummyInvoker,
            meta: &DUMMY_META,
            children: Vec::new(),
            offset: 0,
            method: "",
            path: "",
            #[cfg(feature = "ordinary-http")]
            ordinary_def: None,
        }
    }

    /// Adds a child handler to this node.
    #[doc(hidden)]
    pub fn handler(mut self, handler: Handler) -> Self {
        self.children.push(handler);
        self
    }

    /// Sets the HTTP method for this node (ordinary HTTP routes only).
    #[doc(hidden)]
    pub fn method(mut self, method: &'static str) -> Self {
        self.method = method;
        self
    }

    /// Sets the route path for this node (ordinary HTTP routes only).
    #[doc(hidden)]
    pub fn path(mut self, path: &'static str) -> Self {
        self.path = path;
        self
    }

    /// Creates a leaf [`Handler`] node from an ordinary HTTP route definition.
    ///
    /// Used by the `service!` macro for ordinary HTTP endpoints. The node
    /// uses a [`DummyInvoker`] because ordinary handlers are dispatched
    /// through a separate routing path.
    #[doc(hidden)]
    #[cfg(feature = "ordinary-http")]
    pub fn ordinary_leaf(path: &'static str, method: &'static str, entry: HandlerEntry) -> Self {
        Self {
            name: entry.name,
            invoker: &DummyInvoker,
            meta: entry.meta,
            children: Vec::new(),
            offset: 0,
            method,
            path,
            ordinary_def: Some(entry),
        }
    }
}

/// Receiver for reading messages in long-connection (WebSocket/TCP) handlers.
///
/// Used as a handler parameter to receive binary messages from the client.
///
/// # Example
///
/// ```
/// use afast::handler::Receiver;
/// let (tx, rx) = tokio::sync::mpsc::channel::<Vec<u8>>(32);
/// let mut receiver = Receiver::new(rx);
/// ```
pub struct Receiver {
    rx: tokio::sync::mpsc::Receiver<Vec<u8>>,
}

impl Receiver {
    /// Wraps a tokio mpsc receiver into an afast [`Receiver`].
    pub fn new(rx: tokio::sync::mpsc::Receiver<Vec<u8>>) -> Self {
        Self { rx }
    }

    /// Creates a placeholder receiver for internal use when a handler
    /// parameter slot needs a default value.
    pub fn placeholder() -> Self {
        let (_, rx) = tokio::sync::mpsc::channel(1);
        Self { rx }
    }

    /// Receives the next message from the client.
    ///
    /// Returns `None` when the connection is closed.
    pub async fn recv(&mut self) -> Option<Vec<u8>> {
        self.rx.recv().await
    }
}

/// Sender for writing messages in long-connection (WebSocket/TCP) handlers.
///
/// Used as a handler parameter to send binary messages to the client.
///
/// # Example
///
/// ```
/// use afast::handler::Sender;
/// let (tx, rx) = tokio::sync::mpsc::channel::<Vec<u8>>(32);
/// let sender = Sender::new(tx);
/// ```
pub struct Sender {
    tx: tokio::sync::mpsc::Sender<Vec<u8>>,
}

impl Sender {
    /// Wraps a tokio mpsc sender into an afast [`Sender`].
    pub fn new(tx: tokio::sync::mpsc::Sender<Vec<u8>>) -> Self {
        Self { tx }
    }

    /// Creates a placeholder sender for internal use when a handler parameter
    /// slot needs a default value.
    pub fn placeholder() -> Self {
        let (tx, _) = tokio::sync::mpsc::channel(1);
        Self { tx }
    }

    /// Sends a binary message to the client.
    ///
    /// Returns an error if the connection has been closed.
    pub async fn send(&self, data: Vec<u8>) -> Result<(), Error> {
        self.tx.send(data).await.map_err(|_| Error::Ws {
            message: "channel closed".into(),
        })
    }
}

// ─── Tag (struct field metadata for TS codegen) ────────────────

/// A client-side validation rule for a struct field.
///
/// Generated by `#[derive(Tag)]` from `#[afast(...)]` attributes on struct
/// fields. Used by TypeScript and JavaScript code generators to emit
/// pre-flight validation logic.
#[derive(Debug, Clone, Copy)]
pub enum ValidateRule {
    /// Field value must be greater than `value`.
    Gt {
        value: f64,
        code: i64,
        message: &'static str,
    },
    /// Field value must be greater than or equal to `value`.
    Gte {
        value: f64,
        code: i64,
        message: &'static str,
    },
    /// Field value must be less than `value`.
    Lt {
        value: f64,
        code: i64,
        message: &'static str,
    },
    /// Field value must be less than or equal to `value`.
    Lte {
        value: f64,
        code: i64,
        message: &'static str,
    },
    /// Field length (string or array) must be between `min` and `max`
    /// inclusive. A negative `min` means no lower bound; a negative `max`
    /// means no upper bound.
    Len {
        min: i64,
        max: i64,
        code: i64,
        message: &'static str,
    },
    /// Field value must be one of the listed string values.
    Of {
        values: &'static [&'static str],
        code: i64,
        message: &'static str,
    },
}

/// Metadata for a single field in a [`TagKind::Struct`].
///
/// Used by TypeScript and JavaScript code generators to emit type definitions.
/// The `structure` function pointer enables recursive discovery of nested
/// complex types without a global registry.
#[derive(Debug, Clone, Copy)]
pub struct FieldMeta {
    /// Field name as declared in the struct.
    pub name: &'static str,
    /// Rust type as a string (e.g. `"String"`, `"Vec<User>"`).
    pub ty: &'static str,
    /// Field description from the `#[tag("...")]` attribute on the type.
    pub desc: &'static str,
    /// Function pointer returning [`TagMeta`] for nested complex types.
    /// Enables recursive type discovery during code generation.
    pub structure: Option<fn() -> &'static TagMeta>,
    /// Client-side validation rules from `#[afast(...)]` attributes.
    pub validations: &'static [ValidateRule],
}

/// Metadata for a single variant in a [`TagKind::Enum`].
#[derive(Debug, Clone, Copy)]
pub struct EnumVariantMeta {
    /// Variant name (e.g. `"Admin"`, `"User"`).
    pub name: &'static str,
    /// Fields of this variant. Empty for unit variants and tuple variants
    /// with no named fields.
    pub fields: &'static [FieldMeta],
}

/// Discriminant for tagged types: either a struct or an enum.
#[derive(Debug, Clone, Copy)]
pub enum TagKind {
    /// A struct with named fields.
    Struct(&'static [FieldMeta]),
    /// An enum with named variants.
    Enum(&'static [EnumVariantMeta]),
}

/// Top-level metadata for a tagged type.
///
/// Produced by `#[derive(Tag)]`. Contains the type's name, description, and
/// its kind (struct or enum). Used by TypeScript and JavaScript code
/// generators to emit `export type` or `@typedef` declarations.
#[derive(Debug, Clone, Copy)]
pub struct TagMeta {
    /// Type name (e.g. `"AuthCustom"`).
    pub name: &'static str,
    /// Description from the `#[tag("...")]` attribute.
    pub desc: &'static str,
    /// Whether this is a struct or enum, with field or variant metadata.
    pub kind: TagKind,
}

/// Trait for types that expose their field structure to code generators.
///
/// Implemented automatically by `#[derive(Tag)]`. Provides a static
/// [`TagMeta`] describing the type's fields or enum variants, enabling
/// TypeScript and JavaScript type definition generation.
pub trait Structure {
    /// Returns the static [`TagMeta`] for this type.
    fn structure() -> &'static TagMeta;
}

/// Returns `None` for [`ParamMeta`] entries that have no nested structure
/// (primitive types).
pub const fn no_structure() -> Option<fn() -> &'static TagMeta> {
    None
}
