#pragma once

#include <cstddef>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <vector>

#include <fesa/core/diagnostic.hpp>
#include <fesa/results/result_database.hpp>

namespace fesa {

enum class ReferenceQuantity {
    displacement,
    reaction,
    internal_force,
    centroid_stress
};

struct Tolerance final {
    double relative;
    double absolute_scale;
};

struct ResultPosition final {
    std::string instance_name;
    std::int64_t entity_label;
    std::optional<std::int64_t> end_node_label;
};

struct ComparisonSample final {
    ReferenceQuantity quantity;
    ResultPosition position;
    std::vector<double> reference;
    std::vector<double> actual;
    Tolerance tolerance;
};

struct ComparisonReport final {
    bool passed;
    double maximum_normalized_error;
    std::vector<Diagnostic> failures;
};

struct ComponentCorrelationMetric final {
    ReferenceQuantity quantity;
    std::size_t component_index;
    std::size_t value_count;
    double root_mean_square_error;
    double relative_l2_error;
};

struct CorrelationReport final {
    bool evaluable;
    std::vector<ComponentCorrelationMetric> metrics;
    std::vector<Diagnostic> failures;
};

struct ComparisonSampleMatch final {
    std::optional<ComparisonSample> sample;
    std::vector<Diagnostic> failures;
};

[[nodiscard]] ComparisonSampleMatch make_comparison_sample(
    const ResultFrame& frame,
    ReferenceQuantity quantity,
    const ResultPosition& position,
    std::span<const double> reference,
    Tolerance tolerance);

[[nodiscard]] ComparisonReport compare_samples(
    std::span<const ComparisonSample> samples);

[[nodiscard]] CorrelationReport correlate_samples(
    std::span<const ComparisonSample> samples);

}  // namespace fesa
