#define NOMINMAX
#include <Windows.h>

#include "fesa/io/hdf5/hdf5_results_writer.hpp"

#include "fesa/build_info.hpp"

#include <hdf5.h>

#include <algorithm>
#include <array>
#include <atomic>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <limits>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

namespace fesa {
namespace {

constexpr std::size_t kDofsPerNode = 6U;
constexpr std::size_t kEndpointCount = 2U;
constexpr std::size_t kGaussPointCount = 2U;
constexpr std::size_t kEndActionComponentCount = 6U;
constexpr std::size_t kGeneralizedComponentCount = 4U;
constexpr const char* kStepName = "Step-1";
constexpr std::size_t kFrameIndex = 0U;
constexpr const char* kStepRoot = "/steps/Step-1/frames/0";

class Hdf5Handle {
public:
    using Closer = herr_t (*)(hid_t);

    Hdf5Handle() = default;
    Hdf5Handle(const hid_t value, Closer closer)
        : value_{value}, closer_{closer} {}
    Hdf5Handle(const Hdf5Handle&) = delete;
    Hdf5Handle& operator=(const Hdf5Handle&) = delete;
    Hdf5Handle(Hdf5Handle&& other) noexcept
        : value_{other.value_}, closer_{other.closer_} {
        other.value_ = -1;
        other.closer_ = nullptr;
    }
    Hdf5Handle& operator=(Hdf5Handle&& other) noexcept {
        if (this != &other) {
            reset();
            value_ = other.value_;
            closer_ = other.closer_;
            other.value_ = -1;
            other.closer_ = nullptr;
        }
        return *this;
    }
    ~Hdf5Handle() { reset(); }

    hid_t get() const noexcept { return value_; }
    herr_t closeChecked() noexcept {
        if (value_ < 0 || closer_ == nullptr) {
            return 0;
        }
        const hid_t value = value_;
        const Closer closer = closer_;
        value_ = -1;
        closer_ = nullptr;
        return closer(value);
    }

private:
    void reset() noexcept {
        if (value_ >= 0 && closer_ != nullptr) {
            (void)closer_(value_);
        }
        value_ = -1;
        closer_ = nullptr;
    }

    hid_t value_{-1};
    Closer closer_{nullptr};
};

// Expected backend failures become one structured FESA diagnostic, not an
// HDF5 error-stack dump mixed into deterministic CLI output.
class Hdf5ErrorSilencer {
public:
    Hdf5ErrorSilencer() {
        if (H5Eget_auto2(H5E_DEFAULT, &callback_, &clientData_) >= 0 &&
            H5Eset_auto2(H5E_DEFAULT, nullptr, nullptr) >= 0) {
            active_ = true;
        }
    }
    Hdf5ErrorSilencer(const Hdf5ErrorSilencer&) = delete;
    Hdf5ErrorSilencer& operator=(const Hdf5ErrorSilencer&) = delete;
    ~Hdf5ErrorSilencer() {
        if (active_) {
            (void)H5Eset_auto2(H5E_DEFAULT, callback_, clientData_);
        }
    }

private:
    H5E_auto2_t callback_{nullptr};
    void* clientData_{nullptr};
    bool active_{false};
};

class TemporaryFileGuard {
public:
    explicit TemporaryFileGuard(std::filesystem::path path)
        : path_{std::move(path)} {}
    TemporaryFileGuard(const TemporaryFileGuard&) = delete;
    TemporaryFileGuard& operator=(const TemporaryFileGuard&) = delete;
    ~TemporaryFileGuard() {
        if (active_) {
            std::error_code ignored;
            (void)std::filesystem::remove(path_, ignored);
        }
    }
    void release() noexcept { active_ = false; }

private:
    std::filesystem::path path_;
    bool active_{true};
};

class Hdf5Failure final : public std::runtime_error {
public:
    explicit Hdf5Failure(const std::string& message)
        : std::runtime_error{message} {}
};

void requireHdf5(const herr_t result, const char* message) {
    if (result < 0) {
        throw Hdf5Failure{message};
    }
}

hid_t requireHdf5Id(const hid_t result, const char* message) {
    if (result < 0) {
        throw Hdf5Failure{message};
    }
    return result;
}

Status outputFailure(const std::string& code, const std::string& message) {
    return Status::failure(
        FailureCategory::output,
        {{Severity::error, code, {}, "", "", message}});
}

bool isFinite(const std::array<double, 3>& values) {
    return std::all_of(values.begin(), values.end(), [](const double value) {
        return std::isfinite(value);
    });
}

template<std::size_t Size>
bool isFinite(const std::array<double, Size>& values) {
    return std::all_of(values.begin(), values.end(), [](const double value) {
        return std::isfinite(value);
    });
}

bool isValidUtf8(const std::string& value) {
    const auto* bytes = reinterpret_cast<const unsigned char*>(value.data());
    std::size_t index = 0U;
    while (index < value.size()) {
        const unsigned char first = bytes[index];
        if (first <= 0x7fU) {
            ++index;
            continue;
        }

        std::size_t continuationCount = 0U;
        std::uint32_t codePoint = 0U;
        if (first >= 0xc2U && first <= 0xdfU) {
            continuationCount = 1U;
            codePoint = first & 0x1fU;
        } else if (first >= 0xe0U && first <= 0xefU) {
            continuationCount = 2U;
            codePoint = first & 0x0fU;
        } else if (first >= 0xf0U && first <= 0xf4U) {
            continuationCount = 3U;
            codePoint = first & 0x07U;
        } else {
            return false;
        }
        if (index + continuationCount >= value.size()) {
            return false;
        }
        for (std::size_t offset = 1U; offset <= continuationCount; ++offset) {
            const unsigned char next = bytes[index + offset];
            if ((next & 0xc0U) != 0x80U) {
                return false;
            }
            codePoint = (codePoint << 6U) | (next & 0x3fU);
        }
        if ((continuationCount == 2U && codePoint < 0x800U) ||
            (continuationCount == 3U && codePoint < 0x10000U) ||
            (codePoint >= 0xd800U && codePoint <= 0xdfffU) ||
            codePoint > 0x10ffffU) {
            return false;
        }
        index += continuationCount + 1U;
    }
    return true;
}

bool sameIdentity(const SourceEntityId& left, const SourceEntityId& right) {
    return left.instanceName == right.instanceName &&
           left.sourceLabel == right.sourceLabel &&
           left.sourceLabelText == right.sourceLabelText;
}

using AxisSet = std::array<double, 9>;

bool computeLocalAxes(
    const Domain& domain, const EulerBeam3DDefinition& element, AxisSet& axes) {
    if (element.nodeIndices[0U] >= domain.nodes().size() ||
        element.nodeIndices[1U] >= domain.nodes().size() ||
        element.sectionIndex >= domain.sections().size()) {
        return false;
    }
    const auto& first = domain.nodes()[element.nodeIndices[0U]].coordinates;
    const auto& second = domain.nodes()[element.nodeIndices[1U]].coordinates;
    const auto& guide = domain.sections()[element.sectionIndex].firstAxis;
    const std::array<double, 3> delta = {
        second[0U] - first[0U],
        second[1U] - first[1U],
        second[2U] - first[2U]};
    const double length = std::hypot(delta[0U], delta[1U], delta[2U]);
    if (!isFinite(first) || !isFinite(second) || !isFinite(guide) ||
        !isFinite(delta) || !std::isfinite(length) || !(length > 0.0)) {
        return false;
    }
    const std::array<double, 3> x = {
        delta[0U] / length, delta[1U] / length, delta[2U] / length};
    const double projection =
        guide[0U] * x[0U] + guide[1U] * x[1U] + guide[2U] * x[2U];
    const std::array<double, 3> yTrial = {
        guide[0U] - projection * x[0U],
        guide[1U] - projection * x[1U],
        guide[2U] - projection * x[2U]};
    const double yNorm = std::hypot(yTrial[0U], yTrial[1U], yTrial[2U]);
    if (!isFinite(yTrial) || !std::isfinite(yNorm) || !(yNorm > 0.0)) {
        return false;
    }
    const std::array<double, 3> y = {
        yTrial[0U] / yNorm, yTrial[1U] / yNorm, yTrial[2U] / yNorm};
    const std::array<double, 3> z = {
        x[1U] * y[2U] - x[2U] * y[1U],
        x[2U] * y[0U] - x[0U] * y[2U],
        x[0U] * y[1U] - x[1U] * y[0U]};
    axes = {
        x[0U], x[1U], x[2U],
        y[0U], y[1U], y[2U],
        z[0U], z[1U], z[2U]};
    return isFinite(axes);
}

bool sizeProductFits(
    const std::size_t left, const std::size_t right, std::size_t& product) {
    if (right != 0U && left > (std::numeric_limits<std::size_t>::max)() / right) {
        return false;
    }
    product = left * right;
    return true;
}

Status validateWriterInput(
    const std::filesystem::path& outputPath,
    const Domain& domain,
    const AnalysisState& state,
    const std::vector<Diagnostic>& diagnostics,
    std::vector<AxisSet>& localAxes) {
    if (outputPath.empty() || outputPath.filename().empty()) {
        return outputFailure(
            "invalid-output-path", "The HDF5 output path must name a file.");
    }
    if (state.identity().stepName != kStepName ||
        state.identity().frameIndex != kFrameIndex) {
        return outputFailure(
            "invalid-result-state",
            "Schema v0 requires literal Step-1 and frame index 0.");
    }

    std::size_t fullDofCount = 0U;
    if (!sizeProductFits(domain.nodes().size(), kDofsPerNode, fullDofCount)) {
        return outputFailure(
            "invalid-result-state", "The nodal result shape overflows size_t.");
    }
    const std::array<const Vector*, 5> vectors = {
        &state.displacement(),
        &state.externalForce(),
        &state.internalForce(),
        &state.residual(),
        &state.reaction()};
    for (const Vector* vector : vectors) {
        if (vector->size() != fullDofCount) {
            return outputFailure(
                "invalid-result-state",
                "Every V0 analysis vector must have node_count*6 values.");
        }
        for (std::size_t index = 0U; index < vector->size(); ++index) {
            if (!std::isfinite((*vector)[index])) {
                return outputFailure(
                    "invalid-result-state",
                    "Every V0 analysis vector value must be finite.");
            }
        }
    }

    if (domain.sourcePath().empty() || domain.sourceContentIdentity().empty() ||
        !isValidUtf8(domain.sourceContentIdentity())) {
        return outputFailure(
            "invalid-result-identity",
            "Source path and UTF-8 content identity are required.");
    }
    for (const Node& node : domain.nodes()) {
        if (node.sourceId.sourceLabel <= 0 ||
            node.sourceId.sourceLabelText.empty() ||
            !isValidUtf8(node.sourceId.instanceName) ||
            !isValidUtf8(node.sourceId.sourceLabelText) ||
            !isFinite(node.coordinates)) {
            return outputFailure(
                "invalid-result-identity",
                "Every node requires finite coordinates and UTF-8 source identity.");
        }
    }

    localAxes.clear();
    localAxes.reserve(domain.elements().size());
    for (const EulerBeam3DDefinition& element : domain.elements()) {
        AxisSet axes{};
        if (element.sourceId.sourceLabel <= 0 ||
            element.sourceId.sourceLabelText.empty() ||
            !isValidUtf8(element.sourceId.instanceName) ||
            !isValidUtf8(element.sourceId.sourceLabelText) ||
            element.nodeIndices[0U] == element.nodeIndices[1U] ||
            element.materialIndex >= domain.materials().size() ||
            !computeLocalAxes(domain, element, axes)) {
            return outputFailure(
                "invalid-result-identity",
                "Every element requires valid source, connectivity, property, and local-axis identity.");
        }
        localAxes.push_back(axes);
    }

    std::size_t endpointCount = 0U;
    std::size_t gaussCount = 0U;
    if (!sizeProductFits(domain.elements().size(), kEndpointCount, endpointCount) ||
        !sizeProductFits(domain.elements().size(), kGaussPointCount, gaussCount) ||
        state.endpointResults().size() != endpointCount ||
        state.gaussResults().size() != gaussCount) {
        return outputFailure(
            "invalid-result-rows",
            "Endpoint and Gauss row counts must match every element and location.");
    }
    for (std::size_t rowIndex = 0U;
         rowIndex < state.endpointResults().size();
         ++rowIndex) {
        const EntityIndex expectedElement =
            static_cast<EntityIndex>(rowIndex / kEndpointCount);
        const int expectedEndpoint = static_cast<int>(rowIndex % kEndpointCount);
        const EndpointResultRow& row = state.endpointResults()[rowIndex];
        const auto& element = domain.elements()[expectedElement];
        const auto& expectedNode =
            domain.nodes()[element.nodeIndices[static_cast<std::size_t>(expectedEndpoint)]];
        if (row.element != expectedElement || row.endpoint != expectedEndpoint ||
            !sameIdentity(row.node, expectedNode.sourceId) ||
            !isFinite(row.endAction) || !isFinite(row.sectionResultant)) {
            return outputFailure(
                "invalid-result-rows",
                "Endpoint result rows must follow element/endpoint order and identity.");
        }
    }
    for (std::size_t rowIndex = 0U;
         rowIndex < state.gaussResults().size();
         ++rowIndex) {
        const EntityIndex expectedElement =
            static_cast<EntityIndex>(rowIndex / kGaussPointCount);
        const int expectedGaussPoint =
            static_cast<int>(rowIndex % kGaussPointCount) + 1;
        const GaussResultRow& row = state.gaussResults()[rowIndex];
        if (row.element != expectedElement ||
            row.gaussPoint != expectedGaussPoint ||
            !isFinite(row.generalizedStrain) ||
            !isFinite(row.generalizedResultant)) {
            return outputFailure(
                "invalid-result-rows",
                "Gauss result rows must follow element/Gauss order and identity.");
        }
    }

    std::size_t stressIndex = 0U;
    for (std::size_t elementIndex = 0U;
         elementIndex < domain.elements().size();
         ++elementIndex) {
        const auto& element = domain.elements()[elementIndex];
        const auto& sectionPoints =
            domain.sections()[element.sectionIndex].sectionPoints;
        for (std::size_t gauss = 0U; gauss < kGaussPointCount; ++gauss) {
            const std::size_t count = sectionPoints.empty() ? 1U : sectionPoints.size();
            for (std::size_t point = 0U; point < count; ++point) {
                if (stressIndex >= state.stressResults().size()) {
                    return outputFailure(
                        "invalid-result-rows",
                        "Axial stress rows are missing required element/Gauss/section locations.");
                }
                const StressS11Row& row = state.stressResults()[stressIndex++];
                const std::size_t expectedPoint = sectionPoints.empty() ? 0U : point + 1U;
                const double expectedX1 = sectionPoints.empty() ? 0.0 : sectionPoints[point][0U];
                const double expectedX2 = sectionPoints.empty() ? 0.0 : sectionPoints[point][1U];
                const char* expectedSource = sectionPoints.empty() ? "fesa-default" : "input";
                if (row.element != static_cast<EntityIndex>(elementIndex) ||
                    row.gaussPoint != static_cast<int>(gauss + 1U) ||
                    row.sectionPoint != expectedPoint ||
                    row.x1 != expectedX1 || row.x2 != expectedX2 ||
                    row.source != expectedSource || !isValidUtf8(row.source) ||
                    !std::isfinite(row.x1) || !std::isfinite(row.x2) ||
                    !std::isfinite(row.s11)) {
                    return outputFailure(
                        "invalid-result-rows",
                        "Axial stress rows must follow exact element/Gauss/section identity.");
                }
            }
        }
    }
    if (stressIndex != state.stressResults().size()) {
        return outputFailure(
            "invalid-result-rows", "Axial stress output contains extra rows.");
    }

    for (const Diagnostic& diagnostic : diagnostics) {
        if (!isValidUtf8(diagnostic.code) ||
            !isValidUtf8(diagnostic.keyword) ||
            !isValidUtf8(diagnostic.entityIdentity) ||
            !isValidUtf8(diagnostic.message)) {
            return outputFailure(
                "invalid-result-diagnostic",
                "Diagnostic text must be valid UTF-8.");
        }
    }
    return Status::ok();
}

std::string normalizedPathString(const std::filesystem::path& path) {
    if (path.empty()) {
        return {};
    }
    return std::filesystem::absolute(path).lexically_normal().generic_u8string();
}

std::string sourceInputIdentity(const Domain& domain) {
    return "path=" + normalizedPathString(domain.sourcePath()) +
           ";content_identity=" + domain.sourceContentIdentity();
}

Hdf5Handle makeUtf8StringType() {
    Hdf5Handle type{
        requireHdf5Id(H5Tcopy(H5T_C_S1), "Unable to copy an HDF5 string type."),
        H5Tclose};
    requireHdf5(
        H5Tset_size(type.get(), H5T_VARIABLE),
        "Unable to configure a variable-length HDF5 string.");
    requireHdf5(
        H5Tset_cset(type.get(), H5T_CSET_UTF8),
        "Unable to configure UTF-8 HDF5 strings.");
    requireHdf5(
        H5Tset_strpad(type.get(), H5T_STR_NULLTERM),
        "Unable to configure HDF5 string padding.");
    return type;
}

void writeStringAttribute(
    const hid_t object, const char* name, const std::string& value) {
    auto type = makeUtf8StringType();
    Hdf5Handle space{
        requireHdf5Id(H5Screate(H5S_SCALAR), "Unable to create an attribute space."),
        H5Sclose};
    Hdf5Handle attribute{
        requireHdf5Id(
            H5Acreate2(object, name, type.get(), space.get(), H5P_DEFAULT, H5P_DEFAULT),
            "Unable to create an HDF5 string attribute."),
        H5Aclose};
    const char* raw = value.c_str();
    requireHdf5(
        H5Awrite(attribute.get(), type.get(), &raw),
        "Unable to write an HDF5 string attribute.");
}

void writeUint64Attribute(
    const hid_t object, const char* name, const std::uint64_t value) {
    Hdf5Handle space{
        requireHdf5Id(H5Screate(H5S_SCALAR), "Unable to create an attribute space."),
        H5Sclose};
    Hdf5Handle attribute{
        requireHdf5Id(
            H5Acreate2(object, name, H5T_STD_U64LE, space.get(), H5P_DEFAULT, H5P_DEFAULT),
            "Unable to create an HDF5 integer attribute."),
        H5Aclose};
    requireHdf5(
        H5Awrite(attribute.get(), H5T_NATIVE_UINT64, &value),
        "Unable to write an HDF5 integer attribute.");
}

Hdf5Handle createGroup(const hid_t file, const char* path) {
    Hdf5Handle linkProperties{
        requireHdf5Id(H5Pcreate(H5P_LINK_CREATE), "Unable to create link properties."),
        H5Pclose};
    requireHdf5(
        H5Pset_create_intermediate_group(linkProperties.get(), 1U),
        "Unable to enable intermediate HDF5 groups.");
    return Hdf5Handle{
        requireHdf5Id(
            H5Gcreate2(file, path, linkProperties.get(), H5P_DEFAULT, H5P_DEFAULT),
            "Unable to create an HDF5 group."),
        H5Gclose};
}

Hdf5Handle createDatasetSpace(const std::vector<hsize_t>& dimensions) {
    return Hdf5Handle{
        requireHdf5Id(
            H5Screate_simple(
                static_cast<int>(dimensions.size()), dimensions.data(), nullptr),
            "Unable to create an HDF5 dataset space."),
        H5Sclose};
}

void writeResultAttributes(
    const hid_t dataset,
    const std::string& componentNames,
    const std::string& componentUnits,
    const std::string& coordinateSystem,
    const std::string& location) {
    writeStringAttribute(dataset, "component_names", componentNames);
    writeStringAttribute(
        dataset, "component_unit_dimensions", componentUnits);
    writeStringAttribute(dataset, "coordinate_system", coordinateSystem);
    writeStringAttribute(dataset, "location", location);
    writeStringAttribute(dataset, "step_name", kStepName);
    writeUint64Attribute(dataset, "frame_index", 0U);
}

void writeDoubleDataset(
    const hid_t file,
    const std::string& path,
    const std::vector<hsize_t>& dimensions,
    const double* values,
    const std::size_t valueCount,
    const std::string& componentNames,
    const std::string& componentUnits,
    const std::string& coordinateSystem,
    const std::string& location) {
    auto space = createDatasetSpace(dimensions);
    Hdf5Handle dataset{
        requireHdf5Id(
            H5Dcreate2(
                file, path.c_str(), H5T_IEEE_F64LE, space.get(),
                H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT),
            "Unable to create a floating-point HDF5 dataset."),
        H5Dclose};
    if (valueCount != 0U) {
        requireHdf5(
            H5Dwrite(
                dataset.get(), H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
                H5P_DEFAULT, values),
            "Unable to write a floating-point HDF5 dataset.");
    }
    writeResultAttributes(
        dataset.get(), componentNames, componentUnits, coordinateSystem, location);
}

struct NodeWriteRow {
    std::uint64_t internalNodeId;
    const char* instanceName;
    const char* sourceLabel;
    double coordinates[3];
};

struct ElementWriteRow {
    std::uint64_t internalElementId;
    const char* instanceName;
    const char* sourceLabel;
    std::uint64_t nodeInternalIds[2];
    double localAxes[9];
};

struct StressWriteRow {
    std::uint64_t internalElementId;
    std::uint64_t gaussPointIndex;
    std::uint64_t sectionPointIndex;
    double x1;
    double x2;
    const char* source;
    double s11;
};

struct DiagnosticWriteRow {
    const char* severity;
    const char* code;
    const char* file;
    std::uint64_t line;
    const char* keyword;
    const char* entityIdentity;
    const char* message;
};

Hdf5Handle writeCompoundDataset(
    const hid_t file,
    const char* path,
    const std::size_t rowCount,
    const hid_t fileType,
    const hid_t memoryType,
    const void* rows) {
    const std::vector<hsize_t> dimensions = {
        static_cast<hsize_t>(rowCount)};
    auto space = createDatasetSpace(dimensions);
    Hdf5Handle dataset{
        requireHdf5Id(
            H5Dcreate2(
                file, path, fileType, space.get(), H5P_DEFAULT,
                H5P_DEFAULT, H5P_DEFAULT),
            "Unable to create a compound HDF5 dataset."),
        H5Dclose};
    if (rowCount != 0U) {
        requireHdf5(
            H5Dwrite(
                dataset.get(), memoryType, H5S_ALL, H5S_ALL,
                H5P_DEFAULT, rows),
            "Unable to write a compound HDF5 dataset.");
    }
    return dataset;
}

void writeMetadata(const hid_t file, const Domain& domain) {
    auto metadata = createGroup(file, "/metadata");
    writeUint64Attribute(metadata.get(), "schema_version", 0U);
    writeStringAttribute(
        metadata.get(), "feature_id", "linear-static-3d-euler-beam");
    writeStringAttribute(
        metadata.get(), "solver_version", std::string{solverVersion()});
    writeStringAttribute(
        metadata.get(), "source_input_identity", sourceInputIdentity(domain));
    writeStringAttribute(
        metadata.get(), "unit_system_label", "user-consistent-unspecified");
    writeStringAttribute(
        metadata.get(),
        "coordinate_convention",
        "global-cartesian; beam-local=(t,n1,t-cross-n1)");
    writeStringAttribute(
        metadata.get(), "element_formulation", "B33-3D-Euler-Bernoulli");
    writeStringAttribute(metadata.get(), "step_name", kStepName);
    writeUint64Attribute(metadata.get(), "frame_index", 0U);
}

void writeNodes(const hid_t file, const Domain& domain) {
    std::vector<NodeWriteRow> rows;
    rows.reserve(domain.nodes().size());
    for (std::size_t index = 0U; index < domain.nodes().size(); ++index) {
        const Node& node = domain.nodes()[index];
        rows.push_back({
            static_cast<std::uint64_t>(index),
            node.sourceId.instanceName.c_str(),
            node.sourceId.sourceLabelText.c_str(),
            {node.coordinates[0U], node.coordinates[1U], node.coordinates[2U]}});
    }

    auto stringType = makeUtf8StringType();
    const hsize_t coordinateDimensions[] = {3U};
    Hdf5Handle fileCoordinates{
        requireHdf5Id(
            H5Tarray_create2(H5T_IEEE_F64LE, 1, coordinateDimensions),
            "Unable to create the node coordinate file type."),
        H5Tclose};
    Hdf5Handle memoryCoordinates{
        requireHdf5Id(
            H5Tarray_create2(H5T_NATIVE_DOUBLE, 1, coordinateDimensions),
            "Unable to create the node coordinate memory type."),
        H5Tclose};
    Hdf5Handle fileType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(NodeWriteRow)),
            "Unable to create the node file type."),
        H5Tclose};
    Hdf5Handle memoryType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(NodeWriteRow)),
            "Unable to create the node memory type."),
        H5Tclose};
    requireHdf5(
        H5Tinsert(fileType.get(), "internal_node_id",
                  HOFFSET(NodeWriteRow, internalNodeId), H5T_STD_U64LE),
        "Unable to define the node internal ID field.");
    requireHdf5(
        H5Tinsert(fileType.get(), "instance_name",
                  HOFFSET(NodeWriteRow, instanceName), stringType.get()),
        "Unable to define the node instance field.");
    requireHdf5(
        H5Tinsert(fileType.get(), "source_label",
                  HOFFSET(NodeWriteRow, sourceLabel), stringType.get()),
        "Unable to define the node source-label field.");
    requireHdf5(
        H5Tinsert(fileType.get(), "coordinates",
                  HOFFSET(NodeWriteRow, coordinates), fileCoordinates.get()),
        "Unable to define the node coordinate field.");
    requireHdf5(
        H5Tinsert(memoryType.get(), "internal_node_id",
                  HOFFSET(NodeWriteRow, internalNodeId), H5T_NATIVE_UINT64),
        "Unable to define the node internal ID memory field.");
    requireHdf5(
        H5Tinsert(memoryType.get(), "instance_name",
                  HOFFSET(NodeWriteRow, instanceName), stringType.get()),
        "Unable to define the node instance memory field.");
    requireHdf5(
        H5Tinsert(memoryType.get(), "source_label",
                  HOFFSET(NodeWriteRow, sourceLabel), stringType.get()),
        "Unable to define the node source-label memory field.");
    requireHdf5(
        H5Tinsert(memoryType.get(), "coordinates",
                  HOFFSET(NodeWriteRow, coordinates), memoryCoordinates.get()),
        "Unable to define the node coordinate memory field.");

    auto dataset = writeCompoundDataset(
        file, "/model/nodes", rows.size(), fileType.get(), memoryType.get(),
        rows.data());
    writeStringAttribute(dataset.get(), "coordinate_system", "global-cartesian");
    writeStringAttribute(dataset.get(), "units_label", "length");
}

void writeElements(
    const hid_t file, const Domain& domain, const std::vector<AxisSet>& axes) {
    std::vector<ElementWriteRow> rows;
    rows.reserve(domain.elements().size());
    for (std::size_t index = 0U; index < domain.elements().size(); ++index) {
        const auto& element = domain.elements()[index];
        ElementWriteRow row{
            static_cast<std::uint64_t>(index),
            element.sourceId.instanceName.c_str(),
            element.sourceId.sourceLabelText.c_str(),
            {static_cast<std::uint64_t>(element.nodeIndices[0U]),
             static_cast<std::uint64_t>(element.nodeIndices[1U])},
            {}};
        std::copy(axes[index].begin(), axes[index].end(), row.localAxes);
        rows.push_back(row);
    }

    auto stringType = makeUtf8StringType();
    const hsize_t nodeDimensions[] = {2U};
    const hsize_t axisDimensions[] = {3U, 3U};
    Hdf5Handle fileNodes{
        requireHdf5Id(
            H5Tarray_create2(H5T_STD_U64LE, 1, nodeDimensions),
            "Unable to create the element connectivity file type."),
        H5Tclose};
    Hdf5Handle memoryNodes{
        requireHdf5Id(
            H5Tarray_create2(H5T_NATIVE_UINT64, 1, nodeDimensions),
            "Unable to create the element connectivity memory type."),
        H5Tclose};
    Hdf5Handle fileAxes{
        requireHdf5Id(
            H5Tarray_create2(H5T_IEEE_F64LE, 2, axisDimensions),
            "Unable to create the local-axis file type."),
        H5Tclose};
    Hdf5Handle memoryAxes{
        requireHdf5Id(
            H5Tarray_create2(H5T_NATIVE_DOUBLE, 2, axisDimensions),
            "Unable to create the local-axis memory type."),
        H5Tclose};
    Hdf5Handle fileType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(ElementWriteRow)),
            "Unable to create the element file type."),
        H5Tclose};
    Hdf5Handle memoryType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(ElementWriteRow)),
            "Unable to create the element memory type."),
        H5Tclose};
    const auto insertFields = [&](const hid_t type,
                                  const hid_t integerType,
                                  const hid_t nodeType,
                                  const hid_t axesType) {
        requireHdf5(
            H5Tinsert(type, "internal_element_id",
                      HOFFSET(ElementWriteRow, internalElementId), integerType),
            "Unable to define the element internal ID field.");
        requireHdf5(
            H5Tinsert(type, "instance_name",
                      HOFFSET(ElementWriteRow, instanceName), stringType.get()),
            "Unable to define the element instance field.");
        requireHdf5(
            H5Tinsert(type, "source_label",
                      HOFFSET(ElementWriteRow, sourceLabel), stringType.get()),
            "Unable to define the element source-label field.");
        requireHdf5(
            H5Tinsert(type, "node_internal_ids",
                      HOFFSET(ElementWriteRow, nodeInternalIds), nodeType),
            "Unable to define the element connectivity field.");
        requireHdf5(
            H5Tinsert(type, "local_axes",
                      HOFFSET(ElementWriteRow, localAxes), axesType),
            "Unable to define the element local-axis field.");
    };
    insertFields(fileType.get(), H5T_STD_U64LE, fileNodes.get(), fileAxes.get());
    insertFields(
        memoryType.get(), H5T_NATIVE_UINT64, memoryNodes.get(), memoryAxes.get());
    auto dataset = writeCompoundDataset(
        file, "/model/elements", rows.size(), fileType.get(), memoryType.get(),
        rows.data());
    writeStringAttribute(
        dataset.get(), "formulation", "B33-3D-Euler-Bernoulli");
}

std::vector<double> flattenEndpointValues(
    const std::vector<EndpointResultRow>& rows,
    const bool sectionResultants) {
    const std::size_t components =
        sectionResultants ? kGeneralizedComponentCount : kEndActionComponentCount;
    std::vector<double> values;
    values.reserve(rows.size() * components);
    for (const auto& row : rows) {
        if (sectionResultants) {
            values.insert(
                values.end(), row.sectionResultant.begin(), row.sectionResultant.end());
        } else {
            values.insert(values.end(), row.endAction.begin(), row.endAction.end());
        }
    }
    return values;
}

std::vector<double> flattenGaussValues(
    const std::vector<GaussResultRow>& rows,
    const bool resultants) {
    std::vector<double> values;
    values.reserve(rows.size() * kGeneralizedComponentCount);
    for (const auto& row : rows) {
        const auto& rowValues =
            resultants ? row.generalizedResultant : row.generalizedStrain;
        values.insert(values.end(), rowValues.begin(), rowValues.end());
    }
    return values;
}

void writeStress(const hid_t file, const AnalysisState& state) {
    std::vector<StressWriteRow> rows;
    rows.reserve(state.stressResults().size());
    for (const auto& row : state.stressResults()) {
        rows.push_back({
            static_cast<std::uint64_t>(row.element),
            static_cast<std::uint64_t>(row.gaussPoint),
            static_cast<std::uint64_t>(row.sectionPoint),
            row.x1,
            row.x2,
            row.source.c_str(),
            row.s11});
    }
    auto stringType = makeUtf8StringType();
    Hdf5Handle fileType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(StressWriteRow)),
            "Unable to create the stress file type."),
        H5Tclose};
    Hdf5Handle memoryType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(StressWriteRow)),
            "Unable to create the stress memory type."),
        H5Tclose};
    const auto insertFields = [&](const hid_t type,
                                  const hid_t integerType,
                                  const hid_t doubleType) {
        requireHdf5(
            H5Tinsert(type, "internal_element_id",
                      HOFFSET(StressWriteRow, internalElementId), integerType),
            "Unable to define the stress element field.");
        requireHdf5(
            H5Tinsert(type, "gauss_point_index",
                      HOFFSET(StressWriteRow, gaussPointIndex), integerType),
            "Unable to define the stress Gauss field.");
        requireHdf5(
            H5Tinsert(type, "section_point_index",
                      HOFFSET(StressWriteRow, sectionPointIndex), integerType),
            "Unable to define the stress section-point field.");
        requireHdf5(
            H5Tinsert(type, "x1", HOFFSET(StressWriteRow, x1), doubleType),
            "Unable to define the stress x1 field.");
        requireHdf5(
            H5Tinsert(type, "x2", HOFFSET(StressWriteRow, x2), doubleType),
            "Unable to define the stress x2 field.");
        requireHdf5(
            H5Tinsert(type, "source", HOFFSET(StressWriteRow, source), stringType.get()),
            "Unable to define the stress source field.");
        requireHdf5(
            H5Tinsert(type, "S11", HOFFSET(StressWriteRow, s11), doubleType),
            "Unable to define the stress S11 field.");
    };
    insertFields(fileType.get(), H5T_STD_U64LE, H5T_IEEE_F64LE);
    insertFields(memoryType.get(), H5T_NATIVE_UINT64, H5T_NATIVE_DOUBLE);
    auto dataset = writeCompoundDataset(
        file,
        "/steps/Step-1/frames/0/element/stress_s11",
        rows.size(),
        fileType.get(),
        memoryType.get(),
        rows.data());
    writeResultAttributes(
        dataset.get(), "S11", "force/length^2", "beam-local", "section-point");
}

void writeDiagnostics(
    const hid_t file, const std::vector<Diagnostic>& inputDiagnostics) {
    std::vector<Diagnostic> diagnostics = inputDiagnostics;
    sortDiagnostics(diagnostics);
    std::vector<std::string> files;
    files.reserve(diagnostics.size());
    for (const auto& diagnostic : diagnostics) {
        files.push_back(normalizedPathString(diagnostic.location.file));
    }
    std::vector<DiagnosticWriteRow> rows;
    rows.reserve(diagnostics.size());
    for (std::size_t index = 0U; index < diagnostics.size(); ++index) {
        const auto& diagnostic = diagnostics[index];
        rows.push_back({
            diagnostic.severity == Severity::warning ? "warning" : "error",
            diagnostic.code.c_str(),
            files[index].c_str(),
            static_cast<std::uint64_t>(diagnostic.location.line),
            diagnostic.keyword.c_str(),
            diagnostic.entityIdentity.c_str(),
            diagnostic.message.c_str()});
    }

    auto stringType = makeUtf8StringType();
    Hdf5Handle fileType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(DiagnosticWriteRow)),
            "Unable to create the diagnostic file type."),
        H5Tclose};
    Hdf5Handle memoryType{
        requireHdf5Id(
            H5Tcreate(H5T_COMPOUND, sizeof(DiagnosticWriteRow)),
            "Unable to create the diagnostic memory type."),
        H5Tclose};
    const auto insertFields = [&](const hid_t type, const hid_t integerType) {
        requireHdf5(
            H5Tinsert(type, "severity", HOFFSET(DiagnosticWriteRow, severity),
                      stringType.get()),
            "Unable to define diagnostic severity.");
        requireHdf5(
            H5Tinsert(type, "code", HOFFSET(DiagnosticWriteRow, code),
                      stringType.get()),
            "Unable to define diagnostic code.");
        requireHdf5(
            H5Tinsert(type, "file", HOFFSET(DiagnosticWriteRow, file),
                      stringType.get()),
            "Unable to define diagnostic file.");
        requireHdf5(
            H5Tinsert(type, "line", HOFFSET(DiagnosticWriteRow, line), integerType),
            "Unable to define diagnostic line.");
        requireHdf5(
            H5Tinsert(type, "keyword", HOFFSET(DiagnosticWriteRow, keyword),
                      stringType.get()),
            "Unable to define diagnostic keyword.");
        requireHdf5(
            H5Tinsert(type, "entity_identity",
                      HOFFSET(DiagnosticWriteRow, entityIdentity), stringType.get()),
            "Unable to define diagnostic entity identity.");
        requireHdf5(
            H5Tinsert(type, "message", HOFFSET(DiagnosticWriteRow, message),
                      stringType.get()),
            "Unable to define diagnostic message.");
    };
    insertFields(fileType.get(), H5T_STD_U64LE);
    insertFields(memoryType.get(), H5T_NATIVE_UINT64);
    (void)writeCompoundDataset(
        file, "/diagnostics", rows.size(), fileType.get(), memoryType.get(),
        rows.data());
}

void writeResultDatasets(
    const hid_t file, const Domain& domain, const AnalysisState& state) {
    const std::vector<hsize_t> nodalDimensions = {
        static_cast<hsize_t>(domain.nodes().size()), kDofsPerNode};
    writeDoubleDataset(
        file,
        std::string{kStepRoot} + "/nodal/displacement",
        nodalDimensions,
        state.displacement().data(),
        state.displacement().size(),
        "UX,UY,UZ,URX,URY,URZ",
        "length,length,length,radian,radian,radian",
        "global-cartesian",
        "nodal");
    writeDoubleDataset(
        file,
        std::string{kStepRoot} + "/nodal/reaction",
        nodalDimensions,
        state.reaction().data(),
        state.reaction().size(),
        "RF1,RF2,RF3,RM1,RM2,RM3",
        "force,force,force,force*length,force*length,force*length",
        "global-cartesian",
        "nodal");

    const std::vector<hsize_t> endpointActionDimensions = {
        static_cast<hsize_t>(domain.elements().size()),
        kEndpointCount,
        kEndActionComponentCount};
    const std::vector<hsize_t> generalizedDimensions = {
        static_cast<hsize_t>(domain.elements().size()),
        kEndpointCount,
        kGeneralizedComponentCount};
    const auto endActions = flattenEndpointValues(state.endpointResults(), false);
    writeDoubleDataset(
        file,
        std::string{kStepRoot} + "/element/end_force_local",
        endpointActionDimensions,
        endActions.data(),
        endActions.size(),
        "FX,FY,FZ,MX,MY,MZ",
        "force,force,force,force*length,force*length,force*length",
        "beam-local",
        "endpoint-outward-action");
    const auto sectionResultants =
        flattenEndpointValues(state.endpointResults(), true);
    writeDoubleDataset(
        file,
        std::string{kStepRoot} + "/element/section_resultant",
        generalizedDimensions,
        sectionResultants.data(),
        sectionResultants.size(),
        "N,T,My,Mz",
        "force,force*length,force*length,force*length",
        "beam-local",
        "endpoint-positive-local-x-section-cut");
    const auto generalizedStrains =
        flattenGaussValues(state.gaussResults(), false);
    writeDoubleDataset(
        file,
        std::string{kStepRoot} + "/element/generalized_strain",
        generalizedDimensions,
        generalizedStrains.data(),
        generalizedStrains.size(),
        "epsilon0,kappa_x,kappa_y,kappa_z",
        "1,1/length,1/length,1/length",
        "beam-local",
        "integration-point");
    const auto generalizedResultants =
        flattenGaussValues(state.gaussResults(), true);
    writeDoubleDataset(
        file,
        std::string{kStepRoot} + "/element/generalized_resultant",
        generalizedDimensions,
        generalizedResultants.data(),
        generalizedResultants.size(),
        "N,T,My,Mz",
        "force,force*length,force*length,force*length",
        "beam-local",
        "integration-point");
    writeStress(file, state);
}

void writeFile(
    const std::filesystem::path& temporaryPath,
    const Domain& domain,
    const AnalysisState& state,
    const std::vector<Diagnostic>& diagnostics,
    const std::vector<AxisSet>& localAxes) {
    Hdf5Handle file{
        requireHdf5Id(
            H5Fcreate(
                temporaryPath.string().c_str(), H5F_ACC_EXCL,
                H5P_DEFAULT, H5P_DEFAULT),
            "Unable to create the temporary HDF5 file."),
        H5Fclose};
    writeMetadata(file.get(), domain);
    (void)createGroup(file.get(), "/model");
    (void)createGroup(file.get(), "/steps/Step-1/frames/0/nodal");
    (void)createGroup(file.get(), "/steps/Step-1/frames/0/element");
    writeNodes(file.get(), domain);
    writeElements(file.get(), domain, localAxes);
    writeResultDatasets(file.get(), domain, state);
    writeDiagnostics(file.get(), diagnostics);
    requireHdf5(
        H5Fflush(file.get(), H5F_SCOPE_GLOBAL),
        "Unable to flush the temporary HDF5 file.");
    requireHdf5(
        file.closeChecked(),
        "Unable to close the temporary HDF5 file after writing.");
}

Hdf5Handle openDatasetForCheck(const hid_t file, const char* path) {
    return Hdf5Handle{
        requireHdf5Id(
            H5Dopen2(file, path, H5P_DEFAULT),
            "A mandatory HDF5 dataset is missing."),
        H5Dclose};
}

std::vector<hsize_t> checkedDimensions(const hid_t dataset) {
    Hdf5Handle space{
        requireHdf5Id(
            H5Dget_space(dataset), "Unable to inspect an HDF5 dataspace."),
        H5Sclose};
    const int rank = H5Sget_simple_extent_ndims(space.get());
    if (rank < 0) {
        throw Hdf5Failure{"Unable to inspect an HDF5 dataset rank."};
    }
    std::vector<hsize_t> dimensions(static_cast<std::size_t>(rank));
    if (rank > 0) {
        requireHdf5(
            H5Sget_simple_extent_dims(space.get(), dimensions.data(), nullptr),
            "Unable to inspect HDF5 dataset dimensions.");
    }
    return dimensions;
}

void requireStringAttribute(
    const hid_t object, const char* name, const std::string& expected) {
    Hdf5Handle attribute{
        requireHdf5Id(
            H5Aopen(object, name, H5P_DEFAULT),
            "A mandatory HDF5 string attribute is missing."),
        H5Aclose};
    Hdf5Handle type{
        requireHdf5Id(
            H5Aget_type(attribute.get()), "Unable to inspect an HDF5 attribute type."),
        H5Tclose};
    if (H5Tget_class(type.get()) != H5T_STRING ||
        H5Tis_variable_str(type.get()) <= 0 ||
        H5Tget_cset(type.get()) != H5T_CSET_UTF8) {
        throw Hdf5Failure{"An HDF5 string attribute is not variable-length UTF-8."};
    }
    char* raw = nullptr;
    requireHdf5(
        H5Aread(attribute.get(), type.get(), &raw),
        "Unable to read an HDF5 string attribute.");
    const std::string actual = raw == nullptr ? std::string{} : std::string{raw};
    if (raw != nullptr) {
        requireHdf5(H5free_memory(raw), "Unable to release an HDF5 string value.");
    }
    if (actual != expected) {
        throw Hdf5Failure{"An HDF5 string attribute has the wrong value."};
    }
}

void requireUint64Attribute(
    const hid_t object, const char* name, const std::uint64_t expected) {
    Hdf5Handle attribute{
        requireHdf5Id(
            H5Aopen(object, name, H5P_DEFAULT),
            "A mandatory HDF5 integer attribute is missing."),
        H5Aclose};
    Hdf5Handle type{
        requireHdf5Id(
            H5Aget_type(attribute.get()),
            "Unable to inspect an HDF5 integer attribute type."),
        H5Tclose};
    if (H5Tget_class(type.get()) != H5T_INTEGER ||
        H5Tget_size(type.get()) != sizeof(std::uint64_t) ||
        H5Tget_sign(type.get()) != H5T_SGN_NONE ||
        H5Tequal(type.get(), H5T_STD_U64LE) <= 0) {
        throw Hdf5Failure{"An HDF5 integer attribute is not portable uint64."};
    }
    std::uint64_t actual = 0U;
    requireHdf5(
        H5Aread(attribute.get(), H5T_NATIVE_UINT64, &actual),
        "Unable to read an HDF5 integer attribute.");
    if (actual != expected) {
        throw Hdf5Failure{"An HDF5 integer attribute has the wrong value."};
    }
}

void requirePortableCompoundMember(
    const hid_t compoundType, const unsigned index, const std::string& name) {
    Hdf5Handle memberType{
        requireHdf5Id(
            H5Tget_member_type(compoundType, index),
            "Unable to inspect a compound HDF5 field type."),
        H5Tclose};
    const bool isUint64 =
        name == "internal_node_id" || name == "internal_element_id" ||
        name == "gauss_point_index" || name == "section_point_index" ||
        name == "line";
    const bool isFloat64 = name == "x1" || name == "x2" || name == "S11";
    const bool isString =
        name == "instance_name" || name == "source_label" ||
        name == "source" || name == "severity" || name == "code" ||
        name == "file" || name == "keyword" ||
        name == "entity_identity" || name == "message";
    if (isUint64) {
        if (H5Tget_class(memberType.get()) != H5T_INTEGER ||
            H5Tget_size(memberType.get()) != sizeof(std::uint64_t) ||
            H5Tget_sign(memberType.get()) != H5T_SGN_NONE ||
            H5Tequal(memberType.get(), H5T_STD_U64LE) <= 0) {
            throw Hdf5Failure{"A compound HDF5 field is not portable uint64."};
        }
        return;
    }
    if (isFloat64) {
        if (H5Tget_class(memberType.get()) != H5T_FLOAT ||
            H5Tget_size(memberType.get()) != sizeof(double) ||
            H5Tequal(memberType.get(), H5T_IEEE_F64LE) <= 0) {
            throw Hdf5Failure{"A compound HDF5 field is not float64."};
        }
        return;
    }
    if (isString) {
        if (H5Tget_class(memberType.get()) != H5T_STRING ||
            H5Tis_variable_str(memberType.get()) <= 0 ||
            H5Tget_cset(memberType.get()) != H5T_CSET_UTF8) {
            throw Hdf5Failure{"A compound HDF5 field is not UTF-8."};
        }
        return;
    }

    if (H5Tget_class(memberType.get()) != H5T_ARRAY) {
        throw Hdf5Failure{"A compound HDF5 array field has the wrong type."};
    }
    const int rank = H5Tget_array_ndims(memberType.get());
    if (rank <= 0) {
        throw Hdf5Failure{"Unable to inspect a compound HDF5 array rank."};
    }
    std::vector<hsize_t> dimensions(static_cast<std::size_t>(rank));
    requireHdf5(
        H5Tget_array_dims2(memberType.get(), dimensions.data()),
        "Unable to inspect compound HDF5 array dimensions.");
    Hdf5Handle baseType{
        requireHdf5Id(
            H5Tget_super(memberType.get()),
            "Unable to inspect a compound HDF5 array base type."),
        H5Tclose};
    if (name == "node_internal_ids") {
        if (dimensions != std::vector<hsize_t>{2U} ||
            H5Tequal(baseType.get(), H5T_STD_U64LE) <= 0) {
            throw Hdf5Failure{"Element connectivity is not uint64[2]."};
        }
    } else if (name == "coordinates") {
        if (dimensions != std::vector<hsize_t>{3U} ||
            H5Tequal(baseType.get(), H5T_IEEE_F64LE) <= 0) {
            throw Hdf5Failure{"Node coordinates are not float64[3]."};
        }
    } else if (name == "local_axes") {
        if (dimensions != std::vector<hsize_t>{3U, 3U} ||
            H5Tequal(baseType.get(), H5T_IEEE_F64LE) <= 0) {
            throw Hdf5Failure{"Element local axes are not float64[3,3]."};
        }
    } else {
        throw Hdf5Failure{"A compound HDF5 field has an unknown type contract."};
    }
}

void requireResultAttributes(
    const hid_t dataset,
    const char* componentNames,
    const char* componentUnits,
    const char* coordinateSystem,
    const char* location) {
    requireStringAttribute(dataset, "component_names", componentNames);
    requireStringAttribute(
        dataset, "component_unit_dimensions", componentUnits);
    requireStringAttribute(dataset, "coordinate_system", coordinateSystem);
    requireStringAttribute(dataset, "location", location);
    requireStringAttribute(dataset, "step_name", kStepName);
    requireUint64Attribute(dataset, "frame_index", 0U);
}

void requireDoubleDataset(
    const hid_t file,
    const char* path,
    const std::vector<hsize_t>& expectedDimensions,
    const char* componentNames,
    const char* componentUnits,
    const char* coordinateSystem,
    const char* location) {
    auto dataset = openDatasetForCheck(file, path);
    if (checkedDimensions(dataset.get()) != expectedDimensions) {
        throw Hdf5Failure{"A floating-point HDF5 dataset has the wrong shape."};
    }
    Hdf5Handle type{
        requireHdf5Id(
            H5Dget_type(dataset.get()), "Unable to inspect an HDF5 dataset type."),
        H5Tclose};
    if (H5Tget_class(type.get()) != H5T_FLOAT ||
        H5Tget_size(type.get()) != sizeof(double) ||
        H5Tequal(type.get(), H5T_IEEE_F64LE) <= 0) {
        throw Hdf5Failure{"A result dataset is not IEEE-754 float64 little-endian."};
    }
    requireResultAttributes(
        dataset.get(), componentNames, componentUnits, coordinateSystem, location);
}

void requireCompoundDataset(
    const hid_t file,
    const char* path,
    const hsize_t expectedRows,
    const std::vector<const char*>& expectedMembers) {
    auto dataset = openDatasetForCheck(file, path);
    if (checkedDimensions(dataset.get()) != std::vector<hsize_t>{expectedRows}) {
        throw Hdf5Failure{"A compound HDF5 dataset has the wrong shape."};
    }
    Hdf5Handle type{
        requireHdf5Id(
            H5Dget_type(dataset.get()), "Unable to inspect a compound type."),
        H5Tclose};
    if (H5Tget_class(type.get()) != H5T_COMPOUND ||
        H5Tget_nmembers(type.get()) != static_cast<int>(expectedMembers.size())) {
        throw Hdf5Failure{"A compound HDF5 dataset has the wrong field count."};
    }
    for (std::size_t index = 0U; index < expectedMembers.size(); ++index) {
        char* rawName = H5Tget_member_name(
            type.get(), static_cast<unsigned>(index));
        if (rawName == nullptr) {
            throw Hdf5Failure{"Unable to inspect a compound HDF5 field name."};
        }
        const std::string actualName{rawName};
        requireHdf5(
            H5free_memory(rawName), "Unable to release a compound field name.");
        if (actualName != expectedMembers[index]) {
            throw Hdf5Failure{"A compound HDF5 field has the wrong identity."};
        }
        requirePortableCompoundMember(
            type.get(), static_cast<unsigned>(index), expectedMembers[index]);
    }
}

void selfCheckFile(
    const std::filesystem::path& path,
    const Domain& domain,
    const AnalysisState& state,
    const std::size_t diagnosticCount) {
    if (H5Fis_hdf5(path.string().c_str()) <= 0) {
        throw Hdf5Failure{"The temporary output is not an HDF5 file."};
    }
    Hdf5Handle file{
        requireHdf5Id(
            H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT),
            "Unable to reopen the temporary HDF5 file read-only."),
        H5Fclose};
    {
    Hdf5Handle metadata{
        requireHdf5Id(
            H5Gopen2(file.get(), "/metadata", H5P_DEFAULT),
            "The HDF5 metadata group is missing."),
        H5Gclose};
    requireUint64Attribute(metadata.get(), "schema_version", 0U);
    requireStringAttribute(
        metadata.get(), "feature_id", "linear-static-3d-euler-beam");
    requireStringAttribute(
        metadata.get(), "solver_version", std::string{solverVersion()});
    requireStringAttribute(
        metadata.get(), "source_input_identity", sourceInputIdentity(domain));
    requireStringAttribute(
        metadata.get(), "unit_system_label", "user-consistent-unspecified");
    requireStringAttribute(
        metadata.get(),
        "coordinate_convention",
        "global-cartesian; beam-local=(t,n1,t-cross-n1)");
    requireStringAttribute(
        metadata.get(), "element_formulation", "B33-3D-Euler-Bernoulli");
    requireStringAttribute(metadata.get(), "step_name", kStepName);
    requireUint64Attribute(metadata.get(), "frame_index", 0U);

    requireCompoundDataset(
        file.get(),
        "/model/nodes",
        static_cast<hsize_t>(domain.nodes().size()),
        {"internal_node_id", "instance_name", "source_label", "coordinates"});
    auto nodes = openDatasetForCheck(file.get(), "/model/nodes");
    requireStringAttribute(nodes.get(), "coordinate_system", "global-cartesian");
    requireStringAttribute(nodes.get(), "units_label", "length");
    requireCompoundDataset(
        file.get(),
        "/model/elements",
        static_cast<hsize_t>(domain.elements().size()),
        {"internal_element_id", "instance_name", "source_label",
         "node_internal_ids", "local_axes"});
    auto elements = openDatasetForCheck(file.get(), "/model/elements");
    requireStringAttribute(
        elements.get(), "formulation", "B33-3D-Euler-Bernoulli");

    const std::vector<hsize_t> nodalDimensions = {
        static_cast<hsize_t>(domain.nodes().size()), kDofsPerNode};
    const std::vector<hsize_t> endDimensions = {
        static_cast<hsize_t>(domain.elements().size()),
        kEndpointCount,
        kEndActionComponentCount};
    const std::vector<hsize_t> generalizedDimensions = {
        static_cast<hsize_t>(domain.elements().size()),
        kGaussPointCount,
        kGeneralizedComponentCount};
    requireDoubleDataset(
        file.get(),
        "/steps/Step-1/frames/0/nodal/displacement",
        nodalDimensions,
        "UX,UY,UZ,URX,URY,URZ",
        "length,length,length,radian,radian,radian",
        "global-cartesian",
        "nodal");
    requireDoubleDataset(
        file.get(),
        "/steps/Step-1/frames/0/nodal/reaction",
        nodalDimensions,
        "RF1,RF2,RF3,RM1,RM2,RM3",
        "force,force,force,force*length,force*length,force*length",
        "global-cartesian",
        "nodal");
    requireDoubleDataset(
        file.get(),
        "/steps/Step-1/frames/0/element/end_force_local",
        endDimensions,
        "FX,FY,FZ,MX,MY,MZ",
        "force,force,force,force*length,force*length,force*length",
        "beam-local",
        "endpoint-outward-action");
    requireDoubleDataset(
        file.get(),
        "/steps/Step-1/frames/0/element/section_resultant",
        generalizedDimensions,
        "N,T,My,Mz",
        "force,force*length,force*length,force*length",
        "beam-local",
        "endpoint-positive-local-x-section-cut");
    requireDoubleDataset(
        file.get(),
        "/steps/Step-1/frames/0/element/generalized_strain",
        generalizedDimensions,
        "epsilon0,kappa_x,kappa_y,kappa_z",
        "1,1/length,1/length,1/length",
        "beam-local",
        "integration-point");
    requireDoubleDataset(
        file.get(),
        "/steps/Step-1/frames/0/element/generalized_resultant",
        generalizedDimensions,
        "N,T,My,Mz",
        "force,force*length,force*length,force*length",
        "beam-local",
        "integration-point");
    requireCompoundDataset(
        file.get(),
        "/steps/Step-1/frames/0/element/stress_s11",
        static_cast<hsize_t>(state.stressResults().size()),
        {"internal_element_id", "gauss_point_index", "section_point_index",
         "x1", "x2", "source", "S11"});
    auto stress = openDatasetForCheck(
        file.get(), "/steps/Step-1/frames/0/element/stress_s11");
    requireResultAttributes(
        stress.get(), "S11", "force/length^2", "beam-local", "section-point");
    requireCompoundDataset(
        file.get(),
        "/diagnostics",
        static_cast<hsize_t>(diagnosticCount),
        {"severity", "code", "file", "line", "keyword",
         "entity_identity", "message"});
    }
    requireHdf5(
        file.closeChecked(),
        "Unable to close the read-only HDF5 schema self-check handle.");
}

std::filesystem::path makeTemporaryPath(
    const std::filesystem::path& outputPath) {
    static std::atomic<std::uint64_t> sequence{0U};
    const std::filesystem::path parent = outputPath.parent_path();
    for (std::size_t attempt = 0U; attempt < 1024U; ++attempt) {
        const std::wstring filename =
            L"." + outputPath.filename().wstring() + L".tmp." +
            std::to_wstring(GetCurrentProcessId()) + L"." +
            std::to_wstring(sequence.fetch_add(1U));
        const std::filesystem::path candidate = parent / filename;
        std::error_code error;
        const bool exists = std::filesystem::exists(candidate, error);
        if (error) {
            throw Hdf5Failure{"Unable to inspect the temporary output path."};
        }
        if (!exists) {
            return candidate;
        }
    }
    throw Hdf5Failure{"Unable to allocate a unique temporary output path."};
}

bool finalizeFile(
    const std::filesystem::path& temporaryPath,
    const std::filesystem::path& outputPath) {
    const DWORD attributes = GetFileAttributesW(outputPath.c_str());
    if (attributes != INVALID_FILE_ATTRIBUTES) {
        // ReplaceFileW is the one-operation existing-final policy: failure
        // leaves the old authoritative bytes in place.
        return ReplaceFileW(
                   outputPath.c_str(),
                   temporaryPath.c_str(),
                   nullptr,
                   0U,
                   nullptr,
                   nullptr) != FALSE;
    }
    const DWORD error = GetLastError();
    if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) {
        return false;
    }
    // A new final uses a same-directory write-through move after close/check.
    return MoveFileExW(
               temporaryPath.c_str(),
               outputPath.c_str(),
               MOVEFILE_WRITE_THROUGH) != FALSE;
}

} // namespace

Status Hdf5ResultsWriter::write(
    const std::filesystem::path& outputPath,
    const Domain& domain,
    const AnalysisState& state,
    const std::vector<Diagnostic>& diagnostics) {
    std::vector<AxisSet> localAxes;
    try {
        const Status validation = validateWriterInput(
            outputPath, domain, state, diagnostics, localAxes);
        if (!validation.isOk()) {
            return validation;
        }

        Hdf5ErrorSilencer silenceBackendErrors;
        const std::filesystem::path temporaryPath = makeTemporaryPath(outputPath);
        TemporaryFileGuard cleanup{temporaryPath};
        writeFile(temporaryPath, domain, state, diagnostics, localAxes);
        // Close occurs when writeFile returns; only the reopened read-only file
        // can approve this temp artifact for authoritative replacement.
        selfCheckFile(
            temporaryPath, domain, state, diagnostics.size());
        if (!finalizeFile(temporaryPath, outputPath)) {
            return outputFailure(
                "hdf5-finalization-failure",
                "The checked temporary HDF5 file could not replace the final output.");
        }
        cleanup.release();
        return Status::ok();
    } catch (const Hdf5Failure& failure) {
        return outputFailure("hdf5-write-failure", failure.what());
    } catch (const std::exception& failure) {
        return outputFailure("hdf5-write-failure", failure.what());
    }
}

} // namespace fesa
