#pragma once

#include <array>
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <vector>

#include <fesa/core/diagnostic.hpp>
#include <fesa/core/vec3.hpp>
#include <fesa/model/beam_section.hpp>
#include <fesa/model/domain.hpp>
#include <fesa/model/entity_origin.hpp>
#include <fesa/model/ids.hpp>
#include <fesa/results/result_database.hpp>

namespace fesa {

struct Hdf5NodeSnapshot final {
    std::uint64_t dense_index;
    NodeId id;
    EntityOrigin origin;
    Vec3 coordinates;
};

struct Hdf5ElementSnapshot final {
    std::uint64_t dense_index;
    ElementId id;
    EntityOrigin origin;
    std::array<std::uint64_t, 2> connectivity;
    MaterialId material;
    SectionId section;
};

struct Hdf5SectionSnapshot final {
    SectionId id;
    std::string name;
    double area;
    double iy;
    double iz;
    double torsion_j;
    double shear_area_y;
    double shear_area_z;
    ShearPropertySource shear_source;
    Vec3 orientation;
    std::vector<std::array<double, 2>> recovery_points;
};

struct Hdf5ModelSnapshot final {
    std::vector<Hdf5NodeSnapshot> nodes;
    std::vector<Hdf5ElementSnapshot> elements;
    std::vector<IsotropicElastic> materials;
    std::vector<Hdf5SectionSnapshot> sections;
    std::vector<NodeSet> node_sets;
    std::vector<ElementSet> element_sets;
};

struct Hdf5MetadataSnapshot final {
    std::string schema_version;
    std::string fesa_version;
    std::string unit_policy;
    std::string input_source;
    std::string input_fingerprint;
};

struct Hdf5InputIdentity final {
    std::string source;
    std::string fingerprint;
};

struct Hdf5SolverSettingsSnapshot final {
    std::string backend;
    std::string matrix_storage;
    std::string matrix_type;
    std::string constraint_method;
    std::string assembly;
};

struct Hdf5AnalysisSnapshot final {
    StepDefinition step;
    Hdf5SolverSettingsSnapshot solver;
};

struct Hdf5ReadResult final {
    std::optional<ResultDatabase> database;
    std::vector<Diagnostic> diagnostics;
    std::optional<Hdf5ModelSnapshot> model;
    std::optional<Hdf5MetadataSnapshot> metadata;
    std::optional<Hdf5AnalysisSnapshot> analysis;
};

[[nodiscard]] std::vector<Diagnostic> write_hdf5(
    const std::filesystem::path& path,
    const Domain& domain,
    const ResultDatabase& database,
    const Hdf5InputIdentity& input_identity);

[[nodiscard]] Hdf5ReadResult read_hdf5_results(
    const std::filesystem::path& path);

}  // namespace fesa
