#pragma once

#include "fl/stl/json.h"
#include "fl/stl/stdint.h"
#include "fl/stl/cstddef.h"
#include "fl/stl/move.h"
#include "fl/stl/string.h"
#include "fl/stl/unordered_map.h"
#include "fl/stl/vector.h"
#include "fl/stl/strstream.h"  // IWYU pragma: keep
#include "fl/remote/rpc/rpc.h"
#include "fl/remote/rpc/rpc_mode.h"
#include "fl/remote/rpc/server.h"
#include "fl/remote/types.h"
#include "fl/net/rpc_scheduler.h"
#include "fl/stl/noexcept.h"

namespace fl {

// Forward declarations
class ResponseSend;

/**
 * @brief JSON-RPC server with scheduling support
 *
 * Extends Server with JSON-RPC method dispatch and time-based scheduling.
 * Supports immediate and scheduled execution via "timestamp" field in requests.
 *
 * Architecture:
 *   - Server: JSON-RPC I/O coordination (pull/push)
 *   - Rpc: Method registry and JSON-RPC execution
 *   - RpcScheduler: Time-based task execution
 *   - Remote: Coordinator that combines all three
 *
 * Usage:
 *   Remote remote(requestSource, responseSink);
 *   remote.bind("setLed", [](int i, int r, int g, int b) { leds[i] = CRGB(r,g,b); });
 *   remote.update(millis());  // pull + tick + push
 */
class Remote : public Server {
public:
    /// Configuration for method registration (forwards to Rpc::Config)
    template<typename Callable>
    using Config = typename fl::Rpc::Config<Callable>;

    // Import types from various sources
    using RpcResult = fl::RpcResult;      // from fl/remote/types.h
    using ClearFlags = fl::RemoteClearFlags;    // from fl/remote/types.h
    using MethodInfo = fl::MethodInfo;    // from fl/remote/rpc/rpc.h
    using ParamInfo = fl::ParamInfo;      // from fl/remote/rpc/rpc.h

    /**
     * @brief Construct with I/O callbacks
     * @param source Function that returns next JSON-RPC request (or nullopt if none)
     * @param sink Function that handles outgoing JSON-RPC responses
     *
     * Example (Serial):
     *   fl::Remote remote(
     *       [&]() { return parseJsonRpcFromSerial(); },
     *       [](const fl::json& r) { writeJsonRpcToSerial(r); }
     *   );
     *
     * Example (HTTP Streaming):
     *   auto transport = fl::make_shared<HttpStreamClient>("localhost", 8080);
     *   fl::Remote remote(
     *       [&transport]() { return transport->readRequest(); },
     *       [&transport](const fl::json& r) { transport->writeResponse(r); }
     *   );
     *   // In main loop: transport->update(millis()); remote.update(millis());
     */
    Remote(RequestSource source, ResponseSink sink);

    // Non-copyable, non-movable (lambda captures 'this')
    Remote(const Remote&) FL_NOEXCEPT = delete;
    Remote(Remote&&) FL_NOEXCEPT = delete;
    Remote& operator=(const Remote&) FL_NOEXCEPT = delete;
    Remote& operator=(Remote&&) FL_NOEXCEPT = delete;

    // =========================================================================
    // Method Registration
    // =========================================================================

    /// Register method with config (name, function, optional metadata)
    template<typename Callable>
    void bind(const Config<Callable>& config) {
        mRpc.bind(config);
    }

    /// Register method by name, function, and optional mode (default SYNC)
    template<typename Callable>
    void bind(const char* name, Callable fn, fl::RpcMode mode = fl::RpcMode::SYNC) {
        bind(fl::Rpc::Config<Callable>{name, fl::move(fn), mode});
    }

    /// Register async method with ResponseSend& parameter (for ASYNC/ASYNC_STREAM modes)
    /// Signature: void(ResponseSend&, const json&)
    /// The ResponseSend& parameter provides send(), sendUpdate(), sendFinal() methods
    void bindAsync(const char* name,
                   fl::function<void(fl::ResponseSend&, const fl::json&)> fn,
                   fl::RpcMode mode = fl::RpcMode::ASYNC) {
        mRpc.bindAsync(name, fl::move(fn), mode);
    }

    /// Get bound method by name for direct C++ invocation
    template<class Sig>
    fl::BindResult<Sig> get(const char* name) const {
        return mRpc.get<Sig>(name);
    }

    /// Check if method is registered
    bool has(const fl::string& name) const;

    /// Unregister method by name
    bool unbind(const fl::string& name);

    // =========================================================================
    // RPC Processing
    // =========================================================================

    /// Process JSON-RPC request (with optional "timestamp" field for scheduling)
    /// Returns JSON-RPC response: {"result": ...} or {"error": {...}}
    fl::json processRpc(const fl::json& request);

    // =========================================================================
    // Server Coordination
    // =========================================================================

    /// Main update: pull + tick + push (overrides Server::update)
    size_t update(u32 currentTimeMs);

    /// Process scheduled calls (call regularly)
    size_t tick(u32 currentTimeMs);

    // Note: pull() and push() inherited from Server<fl::json, fl::json>

    // =========================================================================
    // Results and State
    // =========================================================================

    /// Get number of pending scheduled calls
    size_t pendingCount() const;

    /// Clear state (bitwise OR of ClearFlags)
    void clear(ClearFlags flags);

    // =========================================================================
    // Schema
    // =========================================================================

    /// Get method information for all registered methods
    fl::vector<MethodInfo> methods() const;

    /// Returns flat schema document
    /// Format: {"schema": [["methodName", "returnType", [["param1", "type1"], ...]], ...]}
    fl::json schema() const {
        return mRpc.schema();
    }

    /// Get number of registered methods
    fl::size count() const {
        return mRpc.count();
    }

    // =========================================================================
    // Async Response Support
    // =========================================================================

    /// Send async response for a previously-called async method
    /// The request ID is automatically retrieved from internal storage
    void sendAsyncResponse(const char* method, const fl::json& result);

    /// Send stream update for a streaming async method (ASYNC_STREAM mode)
    /// The request ID is automatically retrieved from internal storage
    void sendStreamUpdate(const char* method, const fl::json& update);

    /// Send final stream response for a streaming async method (ASYNC_STREAM mode)
    /// The request ID is automatically retrieved from internal storage and method is removed
    void sendStreamFinal(const char* method, const fl::json& result);

    // =========================================================================
    // Error Reporting (Unsolicited Notifications)
    // =========================================================================

    /// Send an error notification to the remote peer. This is an unsolicited
    /// JSON-RPC notification (no request ID) that bypasses normal RPC flow.
    /// Use this for error-only state reporting where logging would fail due
    /// to serial backpressure.
    /// Format: {"jsonrpc":"2.0","method":"__error","params":{"message":"..."}}
    void reportError(const fl::string& message);

    /// Send an error notification with structured data
    /// Format: {"jsonrpc":"2.0","method":"__error","params":<data>}
    void reportError(const fl::json& data);

protected:
    // Storage for async request IDs (method name -> request ID)
    struct AsyncRequest {
        int requestId;
        u32 timestamp;
    };
    fl::unordered_map<fl::string, AsyncRequest> mAsyncRequests;
    void scheduleFunction(u32 timestamp, u32 receivedAt, const fl::json& jsonRpcRequest);
    void recordResult(const fl::string& funcName, const fl::json& result, u32 scheduledAt, u32 receivedAt, u32 executedAt, bool wasScheduled);

    // Method registry and execution
    fl::Rpc mRpc;

    // Generic task scheduler
    fl::net::RpcScheduler<> mScheduler;

    // RPC-specific result tracking
    fl::vector<RpcResult> mResults;

    // Note: mRequestSource, mResponseSink, mOutgoingQueue inherited from Server
};

} // namespace fl
