// Unit tests para el modelo extendido de graph_types (issue 0049e).
// Cubre: helpers, defaults, manipulacion de flags, lookup color/shape/size
// con y sin override sobre la tabla EntityType / RelationType.

#define CATCH_CONFIG_MAIN
#include "catch_amalgamated.hpp"

#include "viz/graph_types.h"

#include <cstdint>

TEST_CASE("graph_node() defaults", "[viz][graph_types]") {
    GraphNode n = graph_node();
    REQUIRE(n.x == 0.0f);
    REQUIRE(n.y == 0.0f);
    REQUIRE(n.vx == 0.0f);
    REQUIRE(n.vy == 0.0f);
    REQUIRE(n.type_id == 0);
    REQUIRE(n.shape_override == SHAPE_USE_TYPE);
    REQUIRE(n.flags == NF_VISIBLE);
    REQUIRE(n.color_override == 0u);
    REQUIRE(n.size_override == 0.0f);
    REQUIRE(n.label_idx == 0u);
    REQUIRE(n.user_data == 0ull);
}

TEST_CASE("graph_node() acepta type_id custom", "[viz][graph_types]") {
    GraphNode n = graph_node(10.0f, 20.0f, 7);
    REQUIRE(n.x == 10.0f);
    REQUIRE(n.y == 20.0f);
    REQUIRE(n.type_id == 7);
    REQUIRE((n.flags & NF_VISIBLE) != 0);
}

TEST_CASE("graph_edge() defaults", "[viz][graph_types]") {
    GraphEdge e = graph_edge(3, 5);
    REQUIRE(e.source == 3);
    REQUIRE(e.target == 5);
    REQUIRE(e.weight == 1.0f);
    REQUIRE(e.type_id == 0);
    REQUIRE(e.style_override == EDGE_USE_TYPE);
    REQUIRE(e.flags == EF_VISIBLE);
    REQUIRE(e.label_idx == 0u);
}

TEST_CASE("Manipulacion de NodeFlags es bitmask", "[viz][graph_types][flags]") {
    GraphNode n = graph_node();
    REQUIRE((n.flags & NF_PINNED) == 0);

    n.flags |= NF_PINNED;
    REQUIRE((n.flags & NF_PINNED) != 0);
    REQUIRE((n.flags & NF_VISIBLE) != 0);  // VISIBLE no cambia

    n.flags |= NF_HOVERED | NF_SELECTED;
    REQUIRE((n.flags & NF_PINNED) != 0);
    REQUIRE((n.flags & NF_HOVERED) != 0);
    REQUIRE((n.flags & NF_SELECTED) != 0);

    n.flags &= ~NF_HOVERED;
    REQUIRE((n.flags & NF_HOVERED) == 0);
    REQUIRE((n.flags & NF_SELECTED) != 0);
}

TEST_CASE("Manipulacion de EdgeFlags es bitmask", "[viz][graph_types][flags]") {
    GraphEdge e = graph_edge(0, 1);
    REQUIRE((e.flags & EF_DIRECTED) == 0);

    e.flags |= EF_DIRECTED;
    REQUIRE((e.flags & EF_DIRECTED) != 0);
    REQUIRE((e.flags & EF_VISIBLE) != 0);

    e.flags &= ~EF_VISIBLE;
    REQUIRE((e.flags & EF_VISIBLE) == 0);
    REQUIRE((e.flags & EF_DIRECTED) != 0);
}

TEST_CASE("resolve_node_color: override gana sobre EntityType",
          "[viz][graph_types][resolve]") {
    EntityType types[] = {
        entity_type(0xFFAABBCCu, SHAPE_CIRCLE, 5.0f, "a"),
        entity_type(0xFF112233u, SHAPE_SQUARE, 6.0f, "b"),
    };
    GraphNode n = graph_node(0, 0, 1);

    // Sin override: usa el tipo
    REQUIRE(resolve_node_color(n, types, 2) == 0xFF112233u);

    // Con override: gana siempre
    n.color_override = 0xFFFFFFFFu;
    REQUIRE(resolve_node_color(n, types, 2) == 0xFFFFFFFFu);

    // Sin tabla: cae al fallback gris
    GraphNode m = graph_node();
    REQUIRE(resolve_node_color(m, nullptr, 0) == 0xFF888888u);
}

TEST_CASE("resolve_node_shape: override y sentinel",
          "[viz][graph_types][resolve]") {
    EntityType types[] = {
        entity_type(0xFFAAAAAAu, SHAPE_HEX, 5.0f),
    };
    GraphNode n = graph_node(0, 0, 0);

    // shape_override = SHAPE_USE_TYPE (0) → usa el tipo
    REQUIRE(resolve_node_shape(n, types, 1) == SHAPE_HEX);

    // override no-cero → gana
    n.shape_override = SHAPE_DIAMOND;
    REQUIRE(resolve_node_shape(n, types, 1) == SHAPE_DIAMOND);

    // type_id fuera de rango con override → respeta override
    n.type_id = 99;
    REQUIRE(resolve_node_shape(n, types, 1) == SHAPE_DIAMOND);

    // type_id fuera de rango sin override → fallback CIRCLE
    n.shape_override = SHAPE_USE_TYPE;
    REQUIRE(resolve_node_shape(n, types, 1) == SHAPE_CIRCLE);
}

TEST_CASE("resolve_node_size: override > 0 gana, <=0 hereda",
          "[viz][graph_types][resolve]") {
    EntityType types[] = { entity_type(0u, SHAPE_CIRCLE, 8.0f) };
    GraphNode n = graph_node();

    REQUIRE(resolve_node_size(n, types, 1) == 8.0f);

    n.size_override = 12.0f;
    REQUIRE(resolve_node_size(n, types, 1) == 12.0f);

    // valor "negativo" se trata como sentinel → hereda
    n.size_override = -1.0f;
    REQUIRE(resolve_node_size(n, types, 1) == 8.0f);

    // sin tabla y sin override → fallback 4.0
    GraphNode m = graph_node();
    REQUIRE(resolve_node_size(m, nullptr, 0) == 4.0f);
}

TEST_CASE("resolve_edge_color/style/width siguen al RelationType",
          "[viz][graph_types][resolve]") {
    RelationType rels[] = {
        relation_type(0xFF445566u, EDGE_DASHED, 2.5f, "owns"),
    };
    GraphEdge e = graph_edge(0, 1);

    REQUIRE(resolve_edge_color(e, rels, 1) == 0xFF445566u);
    REQUIRE(resolve_edge_style(e, rels, 1) == EDGE_DASHED);
    REQUIRE(resolve_edge_width(e, rels, 1) == 2.5f);

    e.style_override = EDGE_DOTTED;
    REQUIRE(resolve_edge_style(e, rels, 1) == EDGE_DOTTED);

    // Sin tabla: defaults razonables
    REQUIRE(resolve_edge_color(e, nullptr, 0) == 0xFF888888u);
    REQUIRE(resolve_edge_width(e, nullptr, 0) == 1.0f);
}

TEST_CASE("find_node_by_user_data busca lineal", "[viz][graph_types]") {
    GraphNode nodes[3];
    nodes[0] = graph_node(); nodes[0].user_data = 100ull;
    nodes[1] = graph_node(); nodes[1].user_data = 200ull;
    nodes[2] = graph_node(); nodes[2].user_data = 300ull;

    GraphData g{};
    g.nodes = nodes;
    g.node_count = 3;

    REQUIRE(g.find_node_by_user_data(200ull) == 1);
    REQUIRE(g.find_node_by_user_data(300ull) == 2);
    REQUIRE(g.find_node_by_user_data(999ull) == -1);
}

TEST_CASE("update_bounds calcula AABB", "[viz][graph_types]") {
    GraphNode nodes[3];
    nodes[0] = graph_node(-5.0f, -3.0f);
    nodes[1] = graph_node( 4.0f,  7.0f);
    nodes[2] = graph_node( 1.0f,  0.0f);

    GraphData g{};
    g.nodes = nodes;
    g.node_count = 3;
    g.update_bounds();

    REQUIRE(g.min_x == -5.0f);
    REQUIRE(g.max_x ==  4.0f);
    REQUIRE(g.min_y == -3.0f);
    REQUIRE(g.max_y ==  7.0f);
}

TEST_CASE("Tamaños esperados (presupuesto memoria)", "[viz][graph_types]") {
    // GraphNode: 40 bytes en x86_64 — para 100k nodos = ~4 MB. Si crece
    // accidentalmente, este test alerta antes de mergear.
    REQUIRE(sizeof(GraphNode) == 40);
    // GraphEdge: 20 bytes — los uint8_t style/flags caben en el hueco de 2
    // bytes que dejaria el alineado de uint16_t type_id, sin padding extra.
    REQUIRE(sizeof(GraphEdge) == 20);
}
