#pragma once

#include <pxr/usd/usd/stage.h>
#include <pxr/usd/sdf/path.h>
#include <pxr/usd/sdf/valueTypeName.h>
#include <pxr/base/gf/vec2f.h>
#include <string>
#include <vector>

namespace UsdLayerManager {

/// One entry in the shader-node creation menu, derived from the Sdr/Ndr
/// shader registry (covers UsdPreviewSurface, MaterialX, Arnold, Cycles,
/// etc. — whatever shader definitions are discoverable in this build).
struct ShaderNodeTypeInfo {
    std::string identifier; ///< Sdr identifier, authored as info:id
    std::string label;      ///< display label for the create-node menu
    std::string family;     ///< Sdr family (parser-provided, may be empty)
    /// Coarse Hypershade-style bucket ("Material", "Texture", "Geometry",
    /// "Utility", "Light") derived from Sdr context/role/output types; the
    /// create-node list groups by it. Never empty.
    std::string category;
    /// Top-level create-list group derived from the Sdr source type:
    /// "USD" (glslfx/OSL core nodes), "MaterialX", "Arnold", "Cycles", or a
    /// capitalized raw source type. Never empty.
    std::string source;
};

/// One input or output pin on a shader node, with its USD value type so
/// connections/CreateInput/CreateOutput can author the right type.
struct ShaderPinInfo {
    std::string name;
    pxr::SdfValueTypeName typeName;
    /// Sdr-declared allowed values (e.g. UsdUVTexture's sourceColorSpace:
    /// "raw"/"sRGB"/"auto"), empty when the property isn't enum-constrained.
    /// Drives a dropdown instead of a free-text field in the property panel.
    std::vector<std::string> allowedValues;
};

/// Hypershade-style pin display mode for a node in the graph canvas.
/// All: every Sdr-defined pin shown (today's default). Connected: only
/// linked pins shown, the rest collapsed behind a single connectable nub per
/// side. Collapsed: every pin — linked or not — collapses behind that one
/// nub per side, so existing connections visually converge on it too.
enum class PinDisplayMode {
    All = 0,
    Connected = 1,
    Collapsed = 2,
};

/// One UsdShadeShader prim read back from an existing material network.
/// inputs/outputs list the full Sdr-defined pin set (not just authored
/// attributes) so unauthored pins can still be dragged to create a connection.
struct ShaderGraphNode {
    pxr::SdfPath path;
    std::string  shaderId;
    /// Coarse Hypershade-style bucket ("Material", "Texture", "Geometry",
    /// "Utility", "Light") from the same classifier the create-node list uses;
    /// empty when the shader id isn't in the Sdr registry. Drives which nodes
    /// get an in-canvas thumbnail.
    std::string  category;
    pxr::GfVec2f uiPosition{0.0f, 0.0f};
    /// False when no uiPosition custom data is authored (typical for networks
    /// referenced from .mtlx) — the editor auto-lays such nodes out instead.
    bool         hasAuthoredPosition = false;
    /// Pin display mode, read from customData when the "keep graph node view
    /// settings in USD" preference authored it; All when unauthored.
    PinDisplayMode pinDisplayMode = PinDisplayMode::All;
    std::vector<ShaderPinInfo> inputs;
    std::vector<ShaderPinInfo> outputs;
};

/// One authored connection between two shader nodes in the same network.
struct ShaderGraphLink {
    pxr::SdfPath destNode;
    std::string  destInput;
    pxr::SdfPath sourceNode;
    std::string  sourceOutput;
};

struct ShaderGraphSnapshot {
    std::vector<ShaderGraphNode> nodes;
    std::vector<ShaderGraphLink> links;
};

/// Reads/introspects UsdShade material networks and the Sdr shader registry.
/// Mirrors PropertyManager's role: holds read/derive logic only — mutations
/// go through ICommand subclasses in src/core/commands/.
class MaterialManager {
public:
    void SetStage(pxr::UsdStageRefPtr stage) { m_stage = stage; }

    /// All shader node types registered in the Sdr registry, grouped for a
    /// categorized create-node menu. Cached after the first (slow) call.
    const std::vector<ShaderNodeTypeInfo>& GetAvailableShaderNodes();

    /// The entire shader network of the material at materialPath: shaders
    /// under the material (recursing into nested UsdShadeNodeGraphs) plus
    /// everything reachable upstream through connections — including nodes in
    /// node graphs outside the material, as authored by MaterialX (.mtlx)
    /// imports. Connections are resolved through node-graph boundary attrs to
    /// the shader outputs that actually produce the values.
    ShaderGraphSnapshot GetShaderGraph(const pxr::SdfPath& materialPath) const;

    /// Paths of every UsdShadeMaterial prim on the stage, in traversal order.
    /// Drives the material browser list (Hypershade-style).
    std::vector<pxr::SdfPath> GetAllMaterials() const;

private:
    pxr::UsdStageRefPtr m_stage;
    std::vector<ShaderNodeTypeInfo> m_shaderNodeCache;
    bool m_shaderNodeCacheBuilt = false;
};

} // namespace UsdLayerManager
