#pragma once

#include <cstdint>
#include <string>
#include <vector>

#include "viz/graph_types.h"
#include "viz/graph_icons.h"

namespace ge {

// Representacion en memoria de un `types.yaml`:
//
//   entities:
//     - name: Person
//       color: "#5B8DEF"
//       icon: ti-user
//       principal_field: name              # opcional, default = "name"
//       fields:
//         - { name: name,        type: string, required: true }
//         - { name: first_name,  type: string }
//         - { name: age,         type: int }
//         - { name: gender,      type: enum, values: [male, female, other] }
//   relations:
//     - name: owns
//       color: "#888888"
//       style: solid
//
// Campos no presentes quedan con sentinel (`0` para color, `SHAPE_USE_TYPE`
// para shape, `EDGE_USE_TYPE` para style, codepoint = 0 para icon).

// Tipos de campo soportados en el schema. v1 — ampliable. Tipos desconocidos
// se mapean a FK_STRING con warning del parser.
enum FieldKind : uint8_t {
    FK_STRING = 0,
    FK_INT,
    FK_FLOAT,
    FK_BOOL,
    FK_DATE,    // YYYY-MM-DD (validacion blanda en el inspector)
    FK_URL,
    FK_ENUM,    // requiere `values: [...]`
};

const char* field_kind_name(FieldKind k);
FieldKind   field_kind_from_name(const char* s); // case-insensitive; default FK_STRING

struct FieldSpec {
    std::string              name;
    FieldKind                kind     = FK_STRING;
    bool                     required = false;
    std::vector<std::string> enum_values;  // solo si kind == FK_ENUM
};

struct EntitySpec {
    std::string name;
    uint32_t    color        = 0;                 // ABGR; 0 = no override
    uint8_t     shape        = SHAPE_USE_TYPE;    // 0 = no override
    uint16_t    icon_cp      = 0;                 // codepoint Tabler; 0 = sin icono
    std::string icon_name;                        // nombre original ti-* (para round-trip)
    std::string principal_field;                  // vacio = usar "name"
    std::vector<FieldSpec> fields;                // schema; vacio = sin schema
};

struct RelationSpec {
    std::string name;
    uint32_t    color = 0;
    uint8_t     style = EDGE_USE_TYPE;
};

struct ParsedTypes {
    std::vector<EntitySpec>   entities;
    std::vector<RelationSpec> relations;
};

// Parser de `types.yaml`. Devuelve true si pudo abrir/parsear el archivo. En
// fallo, `error_msg` describe el motivo. El parser es tolerante: si un campo
// no es reconocido se ignora silenciosamente.
bool types_load_yaml(const char* path, ParsedTypes* out, std::string* error_msg);

// Writer de `types.yaml`. Escribe el formato compacto de `examples/types.yaml`
// (un dict por entity/relation, sub-keys indentadas, fields como inline-maps).
// Round-trip estable: load → save → load produce el mismo ParsedTypes.
//
// Devuelve true en exito. En fallo, `error_msg` describe el motivo.
bool types_save_yaml(const char* path, const ParsedTypes& types,
                     std::string* error_msg);

// Resuelve un nombre tipo `ti-user` -> codepoint Tabler. Devuelve 0 si no
// existe. La tabla de mapeo vive dentro de la implementacion y solo cubre
// los iconos comunes que el ejemplo OSINT usa; ampliable a demanda.
uint16_t tabler_codepoint_by_name(const char* name);

// Sobrescribe color/shape/icon/style en `graph.types` y `graph.rel_types` si
// hay match por `name`. Tipos no presentes en el yaml se quedan tal cual.
//
// Devuelve la lista de codepoints distintos que la app debera bakear en su
// IconAtlas (orden 1-based: el indice `i` del vector mapea a `icon_id = i+1`).
// Tras llamar `apply_types_yaml`, cada `EntityType.icon_id` referenciado en
// el yaml se ha actualizado al nuevo `icon_id` correspondiente.
//
// Si el yaml no especifica iconos (vector vacio), el grafo se queda sin
// iconos visibles (icon_id = 0 = sin icono).
std::vector<uint16_t> apply_types_yaml(GraphData& graph, const ParsedTypes& types);

// Construye un `IconAtlas` a partir de la lista de codepoints devuelta por
// `apply_types_yaml`. Si la lista esta vacia o el TTF no se encuentra,
// devuelve nullptr (el caller renderiza sin iconos).
IconAtlas* build_icon_atlas(const std::vector<uint16_t>& codepoints);

} // namespace ge
