#pragma once

#include "foundation/result.h"

#include <cstddef>
#include <cstdint>
#include <functional>
#include <array>
#include <vector>

namespace pp::ui {

inline constexpr std::uint32_t invalid_node_slot = 0xffffffffU;
inline constexpr std::uint32_t invalid_connection_slot = 0xffffffffU;

struct NodeHandle {
    std::uint32_t slot = invalid_node_slot;
    std::uint32_t generation = 0;

    [[nodiscard]] constexpr bool valid() const noexcept
    {
        return slot != invalid_node_slot && generation != 0U;
    }
};

[[nodiscard]] constexpr bool operator==(NodeHandle left, NodeHandle right) noexcept
{
    return left.slot == right.slot && left.generation == right.generation;
}

[[nodiscard]] constexpr bool operator!=(NodeHandle left, NodeHandle right) noexcept
{
    return !(left == right);
}

struct UiConnection {
    std::uint32_t slot = invalid_connection_slot;
    std::uint32_t generation = 0;

    [[nodiscard]] constexpr bool valid() const noexcept
    {
        return slot != invalid_connection_slot && generation != 0U;
    }
};

[[nodiscard]] constexpr bool operator==(UiConnection left, UiConnection right) noexcept
{
    return left.slot == right.slot && left.generation == right.generation;
}

[[nodiscard]] constexpr bool operator!=(UiConnection left, UiConnection right) noexcept
{
    return !(left == right);
}

enum class UiCaptureKind : std::uint8_t {
    pointer,
    keyboard,
};

class NodeLifetimeTree;

class ScopedUiConnection {
public:
    ScopedUiConnection() noexcept = default;
    ScopedUiConnection(NodeLifetimeTree& tree, UiConnection connection) noexcept;
    ScopedUiConnection(const ScopedUiConnection&) = delete;
    ScopedUiConnection& operator=(const ScopedUiConnection&) = delete;
    ScopedUiConnection(ScopedUiConnection&& other) noexcept;
    ScopedUiConnection& operator=(ScopedUiConnection&& other) noexcept;
    ~ScopedUiConnection();

    void reset() noexcept;

    [[nodiscard]] UiConnection connection() const noexcept;
    [[nodiscard]] bool connected() const noexcept;

private:
    NodeLifetimeTree* tree_ = nullptr;
    UiConnection connection_ {};
};

class NodeLifetimeTree {
public:
    // Thread-affinity and mutation contract:
    // - Node and callback handles are generation-validated on each lookup.
    // - Destroyed, disconnected, or stale handles are rejected with explicit status.
    // - Mutations may occur during callback dispatch. Callbacks execute from a
    //   captured snapshot, so create/remove connections and destroy subtrees are
    //   safely observed by the current dispatch loop.
    using Callback = std::function<void(NodeHandle)>;

    [[nodiscard]] pp::foundation::Result<NodeHandle> create_root();
    [[nodiscard]] pp::foundation::Result<NodeHandle> create_child(NodeHandle parent);

    [[nodiscard]] bool contains(NodeHandle node) const noexcept;
    [[nodiscard]] pp::foundation::Result<NodeHandle> parent_of(NodeHandle node) const noexcept;
    [[nodiscard]] pp::foundation::Result<std::size_t> child_count(NodeHandle node) const noexcept;
    [[nodiscard]] pp::foundation::Result<NodeHandle> child_at(NodeHandle node, std::size_t index) const noexcept;
    [[nodiscard]] pp::foundation::Status destroy_subtree(NodeHandle node) noexcept;
    void clear() noexcept;

    [[nodiscard]] pp::foundation::Status capture(UiCaptureKind kind, NodeHandle node) noexcept;
    [[nodiscard]] pp::foundation::Status release_capture(UiCaptureKind kind, NodeHandle node) noexcept;
    [[nodiscard]] pp::foundation::Result<NodeHandle> captured_node(UiCaptureKind kind) const noexcept;

    [[nodiscard]] pp::foundation::Result<UiConnection> connect(NodeHandle node, Callback callback);
    [[nodiscard]] pp::foundation::Result<ScopedUiConnection> scoped_connect(NodeHandle node, Callback callback);
    [[nodiscard]] pp::foundation::Status disconnect(UiConnection connection) noexcept;
    [[nodiscard]] pp::foundation::Result<std::size_t> live_connection_count(NodeHandle node) const noexcept;
    [[nodiscard]] pp::foundation::Status dispatch(NodeHandle node);

private:
    struct NodeSlot {
        std::uint32_t generation = 1;
        bool alive = false;
        NodeHandle parent {};
        std::vector<NodeHandle> children;
        std::vector<UiConnection> connections;
    };

    struct ConnectionSlot {
        std::uint32_t generation = 1;
        bool alive = false;
        NodeHandle node {};
        Callback callback;
    };

    [[nodiscard]] NodeSlot* node_slot(NodeHandle node) noexcept;
    [[nodiscard]] const NodeSlot* node_slot(NodeHandle node) const noexcept;
    [[nodiscard]] ConnectionSlot* connection_slot(UiConnection connection) noexcept;
    [[nodiscard]] const ConnectionSlot* connection_slot(UiConnection connection) const noexcept;

    [[nodiscard]] pp::foundation::Result<NodeHandle> allocate_node(NodeHandle parent);
    [[nodiscard]] std::size_t capture_index(UiCaptureKind kind) const noexcept;
    void unlink_from_parent(NodeHandle node) noexcept;
    void release_connection(UiConnection connection) noexcept;
    void release_captures_for_node(NodeHandle node) noexcept;

    std::vector<NodeSlot> nodes_;
    std::vector<std::uint32_t> free_nodes_;
    std::vector<ConnectionSlot> connections_;
    std::vector<std::uint32_t> free_connections_;
    std::array<NodeHandle, 2> captures_ {};
};

}
