//! Handler abstractions and metadata for the afast framework.
//!
//! This module defines the core types that describe handler functions:
//! - [`Handler`] — a node in the handler tree (leaf or group).
//! - [`HandlerEntry`] — a compiled handler reference produced by `register!`.
//! - [`HandlerInvoker`] — trait for type-erased handler dispatch.
//! - [`HandlerMeta`], [`ParamMeta`] — static metadata for code generation.
//! - [`TagMeta`], [`FieldMeta`], [`Structure`] — type metadata for TypeScript
//!   and JavaScript code generators.
//! - [`Receiver`], [`Sender`] — channels for long-connection (WebSocket/TCP) handlers.

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

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

/// Metadata for a single handler parameter.
///
/// Custom attribute value type for handler metadata.
///
/// Stores user-defined attributes from handler macros
/// (e.g. `#[handler(tag("admin"), timeout(30), deprecated)]`).
#[derive(Debug, Clone, Copy)]
pub enum AttrValue {
    /// String value: `tag("admin")` or `tag = "admin"`
    Str(&'static str),
    /// Integer value: `timeout(30)` or `timeout = 30`
    Int(i64),
    /// Boolean value: `deprecated` (bare identifier = true)
    Bool(bool),
}

/// Custom attribute key-value pair for handler metadata.
///
/// Collected from handler macro attributes that are not part of afast's
/// built-in set (`desc`, `name`, `cache`, `rate_limit`).
#[derive(Debug, Clone, Copy)]
pub struct Attr {
    /// Attribute key (e.g. `"tag"`, `"timeout"`, `"deprecated"`)
    pub key: &'static str,
    /// Attribute value
    pub value: AttrValue,
}

/// 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 route 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,
    /// Rate-limit policy ID this handler is bound to. Empty means no rate limiting.
    pub rate_limit_policy: &'static str,
    /// Custom attributes from handler macros (e.g. `tag("admin")`, `deprecated`).
    /// Empty when no custom attributes are defined.
    pub attrs: &'static [Attr],
}

/// 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, request context,
    /// and binary payload.
    ///
    /// Returns the serialized response bytes on success, or an [`Error`] on failure.
    fn call(
        &self,
        state: &StateMap,
        ctx: &crate::ctx::RequestCtx,
        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
    }

    /// Returns the handler's static metadata, if available.
    ///
    /// Generated invokers return `Some(meta)`; dummy/placeholder invokers
    /// return `None`. Used by hooks to access handler name and description.
    fn meta(&self) -> Option<&'static HandlerMeta> {
        None
    }

    /// 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,
        ctx: &'a crate::ctx::RequestCtx,
        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, ctx, 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, shared application state, and
    /// per-request context.
    fn call_ordinary(
        &self,
        req: hyper::Request<hyper::body::Incoming>,
        path_params: &std::collections::HashMap<String, String>,
        query_string: &str,
        state: &StateMap,
        ctx: &crate::ctx::RequestCtx,
    ) -> Pin<Box<dyn Future<Output = OrdinaryResponse> + Send + '_>>;
}

/// Response type for ordinary HTTP handlers.
#[cfg(feature = "ordinary-http")]
pub type OrdinaryResponse =
    Result<hyper::Response<http_body_util::Full<hyper::body::Bytes>>, Error>;

/// 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,
    /// Stable hash-based ID for binary protocol dispatch.
    /// Computed from `fnv1a_32(service_name + "/" + handler_name)`.
    /// Set by the `register!` proc macro when a service name is provided.
    pub stable_id: u32,
    /// 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 {
    /// Sets the stable handler ID.
    #[doc(hidden)]
    pub fn set_stable_id(&mut self, id: u32) {
        self.stable_id = id;
    }

    /// 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,
            stable_id: 0,
            #[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,
            stable_id: 0,
            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 `stable_id` field is a hash-based identifier used for binary
/// protocol dispatch, computed from the full handler path
/// (`service_name + "/" + handler_name`).
#[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>,
    /// Hash-based stable ID for binary protocol dispatch.
    /// Computed from `fnv1a_32(service_name + "/" + handler_path)`.
    pub stable_id: u32,
    /// 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,
    rate_limit_policy: "",
    attrs: &[],
};

/// 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,
        _ctx: &crate::ctx::RequestCtx,
        _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(),
            stable_id: entry.stable_id,
            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(),
            stable_id: 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(),
            stable_id: entry.stable_id,
            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);
/// ```
#[derive(Clone)]
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 }
    }

    /// Returns a reference to the underlying tokio mpsc sender,
    /// allowing it to be cloned for use in push registries.
    pub fn tx(&self) -> &tokio::sync::mpsc::Sender<Vec<u8>> {
        &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],
    /// If `true`, this field is always skipped during serialization
    /// (`#[afast(skip)]`). Client code generators exclude it entirely.
    pub skip: bool,
    /// If non-empty, this field is skipped when the server marker matches
    /// this value (`#[afast(skip_with("marker"))]`). Client code generators
    /// exclude it when the active marker equals this string.
    pub skip_with: &'static str,
}

/// 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
}
