#include "fesa/results/result_recovery.hpp"

#include "fesa/elements/euler_beam_3d.hpp"
#include "fesa/elements/mitc4_shell.hpp"

#include <algorithm>
#include <array>
#include <charconv>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <optional>
#include <stdexcept>
#include <string>
#include <system_error>
#include <utility>
#include <vector>

namespace fesa {
namespace {

constexpr std::size_t kDofsPerNode = 6U;
constexpr std::size_t kElementDofCount = 12U;
constexpr std::size_t kShellElementDofCount = 24U;
constexpr std::size_t kShellLocationCount = 4U;
constexpr double kFreeResidualTolerance = 1.0e-10;
constexpr double kGlobalEquilibriumTolerance = 1.0e-10;
constexpr double kAxisTolerance = 1.0e-12;

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

Status recoveryFailure(const std::string& code,
                       const SourceLocation& location,
                       const std::string& identity,
                       const std::string& message) {
    return Status::failure(
        FailureCategory::model,
        {{Severity::error,
          code,
          location,
          "RESULT_RECOVERY",
          identity,
          message}});
}

template<class T>
Result<T> recoveryResultFailure(const std::string& code,
                                const SourceLocation& location,
                                const std::string& identity,
                                const std::string& message) {
    return Result<T>::failure(
        recoveryFailure(code, location, identity, message));
}

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

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

bool finite(const Vector& values) {
    for (std::size_t index = 0U; index < values.size(); ++index) {
        if (!std::isfinite(values[index])) {
            return false;
        }
    }
    return true;
}

double indexedNorm(const Vector& values,
                   const std::vector<std::size_t>& indices) {
    double result = 0.0;
    for (const std::size_t index : indices) {
        result = std::hypot(result, values[index]);
    }
    return result;
}

std::array<double, 2> freeEquationInternalTermNorms(
    const SparseMatrix& stiffness,
    const Vector& displacement,
    const DofManager& dofs) {
    std::vector<unsigned char> freeColumn(dofs.fullDofCount(), 0U);
    for (const std::size_t fullDof : dofs.freeDofs()) {
        freeColumn[fullDof] = 1U;
    }

    double freeTermNorm = 0.0;
    double constrainedTermNorm = 0.0;
    for (const std::size_t row : dofs.freeDofs()) {
        double freeTerm = 0.0;
        double constrainedTerm = 0.0;
        for (std::size_t position = stiffness.rowOffsets()[row];
             position < stiffness.rowOffsets()[row + 1U];
             ++position) {
            const std::size_t column = stiffness.columnIndices()[position];
            const double contribution =
                stiffness.values()[position] * displacement[column];
            if (freeColumn[column] != 0U) {
                freeTerm += contribution;
            } else {
                constrainedTerm += contribution;
            }
        }
        freeTermNorm = std::hypot(freeTermNorm, freeTerm);
        constrainedTermNorm =
            std::hypot(constrainedTermNorm, constrainedTerm);
    }
    return {freeTermNorm, constrainedTermNorm};
}

bool strictlyIncreasing(const std::vector<std::size_t>& values) {
    return std::adjacent_find(
               values.begin(), values.end(),
               [](const std::size_t left, const std::size_t right) {
                   return left >= right;
               }) == values.end();
}

Status validateRecoveryInputs(const AnalysisModel& model,
                              const DofManager& dofs,
                              const SparseMatrix& fullStiffness,
                              const AnalysisState& state) {
    const Domain& domain = model.domain();
    if (domain.nodes().size() >
        (std::numeric_limits<std::size_t>::max)() / kDofsPerNode) {
        return recoveryFailure(
            "invalid-recovery-dimensions",
            {domain.sourcePath(), 0U},
            domain.sourceContentIdentity(),
            "The semantic node count cannot be represented in full-DOF space.");
    }
    const std::size_t fullCount = domain.nodes().size() * kDofsPerNode;
    if (dofs.fullDofCount() != fullCount ||
        fullStiffness.rows() != fullCount ||
        fullStiffness.columns() != fullCount ||
        state.displacement().size() != fullCount ||
        state.externalForce().size() != fullCount ||
        state.internalForce().size() != fullCount ||
        state.residual().size() != fullCount ||
        state.reaction().size() != fullCount) {
        return recoveryFailure(
            "invalid-recovery-dimensions",
            {domain.sourcePath(), 0U},
            domain.sourceContentIdentity(),
            "Model, DOF, stiffness, and AnalysisState full-space dimensions must agree.");
    }
    const Status matrixStatus = fullStiffness.validate();
    if (!matrixStatus.isOk()) {
        return matrixStatus;
    }
    if (!finite(state.displacement()) || !finite(state.externalForce())) {
        return recoveryFailure(
            "nonfinite-recovery-value",
            {domain.sourcePath(), 0U},
            domain.sourceContentIdentity(),
            "Displacement and external-force inputs must be finite.");
    }

    const auto& freeDofs = dofs.freeDofs();
    const auto& constrainedDofs = dofs.constrainedDofs();
    if (freeDofs.size() != dofs.freeDofCount() ||
        constrainedDofs.size() != dofs.constrainedDofCount() ||
        dofs.prescribedValues().size() != constrainedDofs.size() ||
        freeDofs.size() + constrainedDofs.size() != fullCount ||
        !strictlyIncreasing(freeDofs) ||
        !strictlyIncreasing(constrainedDofs)) {
        return recoveryFailure(
            "invalid-recovery-order",
            {domain.sourcePath(), 0U},
            domain.sourceContentIdentity(),
            "Free and constrained DOFs must form stable increasing full-space orders.");
    }
    std::vector<unsigned char> ownership(fullCount, 0U);
    try {
        for (std::size_t equation = 0U; equation < freeDofs.size(); ++equation) {
            const std::size_t fullDof = freeDofs[equation];
            if (fullDof >= fullCount || ownership[fullDof] != 0U ||
                dofs.freeEquation(fullDof) != equation) {
                return recoveryFailure(
                    "invalid-recovery-order",
                    {domain.sourcePath(), 0U},
                    std::to_string(fullDof),
                    "Free equations must match stable full-DOF order.");
            }
            ownership[fullDof] = 1U;
        }
        for (std::size_t constrained = 0U;
             constrained < constrainedDofs.size();
             ++constrained) {
            const std::size_t fullDof = constrainedDofs[constrained];
            if (fullDof >= fullCount || ownership[fullDof] != 0U ||
                dofs.freeEquation(fullDof).has_value()) {
                return recoveryFailure(
                    "invalid-recovery-order",
                    {domain.sourcePath(), 0U},
                    std::to_string(fullDof),
                    "Constrained DOFs must be unique and absent from free equations.");
            }
            if (!std::isfinite(dofs.prescribedValues()[constrained]) ||
                state.displacement()[fullDof] !=
                    dofs.prescribedValues()[constrained]) {
                return recoveryFailure(
                    "invalid-recovery-state",
                    {domain.sourcePath(), 0U},
                    std::to_string(fullDof),
                    "Constrained displacement must equal its prescribed value before recovery.");
            }
            ownership[fullDof] = 2U;
        }
    } catch (const std::out_of_range&) {
        return recoveryFailure(
            "invalid-recovery-dimensions",
            {domain.sourcePath(), 0U},
            domain.sourceContentIdentity(),
            "DofManager equation storage must cover every full DOF.");
    }
    if (std::find(ownership.begin(), ownership.end(), 0U) != ownership.end()) {
        return recoveryFailure(
            "invalid-recovery-order",
            {domain.sourcePath(), 0U},
            domain.sourceContentIdentity(),
            "Free and constrained DOFs must partition the full range.");
    }

    EntityIndex previousElement = 0U;
    bool firstElement = true;
    for (const EntityIndex element : model.activeElements()) {
        if (element >= domain.elements().size() ||
            (!firstElement && element <= previousElement)) {
            return recoveryFailure(
                "invalid-recovery-entity",
                {domain.sourcePath(), 0U},
                std::to_string(element),
                "Active elements must be unique in stable internal-index order.");
        }
        firstElement = false;
        previousElement = element;
        const auto& definition = domain.elements()[element];
        if (definition.nodeIndices[0U] >= domain.nodes().size() ||
            definition.nodeIndices[1U] >= domain.nodes().size() ||
            definition.materialIndex >= domain.materials().size() ||
            definition.sectionIndex >= domain.sections().size()) {
            return recoveryFailure(
                "invalid-recovery-entity",
                definition.location,
                definition.sourceId.sourceLabelText,
                "Active beam references must resolve before recovery.");
        }
        try {
            const auto& scatter = dofs.elementScatter(element);
            for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) {
                for (std::size_t component = 0U;
                     component < kDofsPerNode;
                     ++component) {
                    const std::size_t expected =
                        static_cast<std::size_t>(definition.nodeIndices[endpoint]) *
                            kDofsPerNode +
                        component;
                    if (scatter[endpoint * kDofsPerNode + component] != expected ||
                        expected >= fullCount) {
                        return recoveryFailure(
                            "invalid-recovery-order",
                            definition.location,
                            definition.sourceId.sourceLabelText,
                            "Element scatter must preserve endpoint/component full-DOF order.");
                    }
                }
            }
        } catch (const std::out_of_range&) {
            return recoveryFailure(
                "invalid-recovery-entity",
                definition.location,
                definition.sourceId.sourceLabelText,
                "Every active element requires one twelve-DOF scatter map.");
        }
    }

    if (!model.activeElements().empty() && !domain.shellElements().empty()) {
        return recoveryFailure(
            "unsupported-mixed-element-model",
            {domain.sourcePath(), 0U},
            "B33:FESA-MITC4",
            "Result recovery does not support mixed beam and shell models.");
    }
    if (domain.shellElements().size() >
        static_cast<std::size_t>(
            (std::numeric_limits<EntityIndex>::max)())) {
        return recoveryFailure(
            "invalid-recovery-dimensions",
            {domain.sourcePath(), 0U},
            domain.sourceContentIdentity(),
            "The shell element count cannot be represented by stable element identities.");
    }
    for (std::size_t elementOrder = 0U;
         elementOrder < domain.shellElements().size();
         ++elementOrder) {
        const auto& definition = domain.shellElements()[elementOrder];
        if (definition.materialIndex >= domain.materials().size() ||
            definition.sectionIndex >= domain.shellSections().size()) {
            return recoveryFailure(
                "invalid-recovery-entity",
                definition.location,
                definition.sourceId.sourceLabelText,
                "Active shell material and section references must resolve before recovery.");
        }
        try {
            const auto& scatter = dofs.shellElementScatter(
                static_cast<EntityIndex>(elementOrder));
            for (std::size_t nodePosition = 0U;
                 nodePosition < definition.nodeIndices.size();
                 ++nodePosition) {
                const EntityIndex node = definition.nodeIndices[nodePosition];
                if (node >= domain.nodes().size()) {
                    return recoveryFailure(
                        "invalid-recovery-entity",
                        definition.location,
                        definition.sourceId.sourceLabelText,
                        "Active shell node references must resolve before recovery.");
                }
                for (std::size_t component = 0U;
                     component < kDofsPerNode;
                     ++component) {
                    const std::size_t local =
                        nodePosition * kDofsPerNode + component;
                    const std::size_t expected =
                        static_cast<std::size_t>(node) * kDofsPerNode +
                        component;
                    if (scatter[local] != expected || expected >= fullCount) {
                        return recoveryFailure(
                            "invalid-recovery-order",
                            definition.location,
                            definition.sourceId.sourceLabelText,
                            "Shell scatter must preserve node/component full-DOF order.");
                    }
                }
            }
        } catch (const std::out_of_range&) {
            return recoveryFailure(
                "invalid-recovery-entity",
                definition.location,
                definition.sourceId.sourceLabelText,
                "Every active shell requires one twenty-four-DOF scatter map.");
        }
    }
    return Status::ok();
}

char asciiLower(const char value) {
    if (value >= 'A' && value <= 'Z') {
        return static_cast<char>(value + ('a' - 'A'));
    }
    return value;
}

bool equalName(const std::string& left, const std::string& right) {
    return left.size() == right.size() &&
           std::equal(
               left.begin(), left.end(), right.begin(),
               [](const char leftValue, const char rightValue) {
                   return asciiLower(leftValue) == asciiLower(rightValue);
               });
}

bool tryPositiveInteger(const std::string& text, std::int64_t& value) {
    const char* const first = text.data();
    const char* const last = first + text.size();
    const auto parsed = std::from_chars(first, last, value);
    return parsed.ec == std::errc{} && parsed.ptr == last && value > 0;
}

Result<std::vector<EntityIndex>> resolveLoadTarget(const Domain& domain,
                                                   const NodalLoad& load) {
    std::vector<const NodeSet*> sets;
    for (const auto& set : domain.nodeSets()) {
        if (equalName(set.name, load.target)) {
            sets.push_back(&set);
        }
    }
    std::vector<EntityIndex> nodes;
    std::int64_t sourceLabel = 0;
    if (tryPositiveInteger(load.target, sourceLabel)) {
        for (std::size_t node = 0U; node < domain.nodes().size(); ++node) {
            if (domain.nodes()[node].sourceId.sourceLabel == sourceLabel) {
                nodes.push_back(static_cast<EntityIndex>(node));
            }
        }
    }
    if (sets.size() > 1U || nodes.size() > 1U ||
        (!sets.empty() && !nodes.empty())) {
        return recoveryResultFailure<std::vector<EntityIndex>>(
            "invalid-node-station-entity",
            load.location,
            load.target,
            "A station-eligibility load target must resolve unambiguously.");
    }
    if (!sets.empty()) {
        std::vector<unsigned char> seen(domain.nodes().size(), 0U);
        for (const EntityIndex node : sets.front()->nodeIndices) {
            if (node >= domain.nodes().size() || seen[node] != 0U) {
                return recoveryResultFailure<std::vector<EntityIndex>>(
                    "invalid-node-station-entity",
                    load.location,
                    load.target,
                    "A station-eligibility node set must contain unique valid nodes.");
            }
            seen[node] = 1U;
        }
        return Result<std::vector<EntityIndex>>::success(
            sets.front()->nodeIndices);
    }
    if (!nodes.empty()) {
        return Result<std::vector<EntityIndex>>::success(std::move(nodes));
    }
    return recoveryResultFailure<std::vector<EntityIndex>>(
        "invalid-node-station-entity",
        load.location,
        load.target,
        "A station-eligibility load target must resolve to a node or node set.");
}

std::array<double, 3> cross(const std::array<double, 3>& left,
                            const std::array<double, 3>& right) {
    return {
        left[1U] * right[2U] - left[2U] * right[1U],
        left[2U] * right[0U] - left[0U] * right[2U],
        left[0U] * right[1U] - left[1U] * right[0U]};
}

double dot(const std::array<double, 3>& left,
           const std::array<double, 3>& right) {
    return left[0U] * right[0U] + left[1U] * right[1U] +
           left[2U] * right[2U];
}

double norm(const std::array<double, 3>& value) {
    return std::hypot(value[0U], value[1U], value[2U]);
}

bool accumulateVectorAndScale(
    std::array<double, 3>& total,
    double& scale,
    const std::array<double, 3>& contribution) {
    const double magnitude = norm(contribution);
    const double accumulatedScale = scale + magnitude;
    if (!finite(contribution) || !std::isfinite(magnitude) ||
        !std::isfinite(accumulatedScale)) {
        return false;
    }
    for (std::size_t component = 0U;
         component < contribution.size();
         ++component) {
        const double accumulated = total[component] + contribution[component];
        if (!std::isfinite(accumulated)) {
            return false;
        }
        total[component] = accumulated;
    }
    scale = accumulatedScale;
    return true;
}

double normalizedBalance(
    const std::array<double, 3>& balance,
    const double scale) {
    const double balanceNorm = norm(balance);
    if (!std::isfinite(balanceNorm) || !std::isfinite(scale)) {
        return (std::numeric_limits<double>::infinity)();
    }
    if (scale == 0.0) {
        return balanceNorm == 0.0
            ? 0.0
            : (std::numeric_limits<double>::infinity)();
    }
    return balanceNorm / scale;
}

Status populateShellGlobalEvidence(
    const Domain& domain,
    const DofManager& dofs,
    const Vector& externalForce,
    const Vector& residual,
    const double normalizedResidual,
    ShellStateCandidate& candidate) {
    std::vector<unsigned char> constrained(dofs.fullDofCount(), 0U);
    for (const std::size_t fullDof : dofs.constrainedDofs()) {
        constrained[fullDof] = 1U;
    }

    std::array<double, 3> appliedForce{};
    std::array<double, 3> reactionForce{};
    std::array<double, 3> appliedMoment{};
    std::array<double, 3> reactionMoment{};
    double appliedForceScale = 0.0;
    double reactionForceScale = 0.0;
    double appliedMomentScale = 0.0;
    double reactionMomentScale = 0.0;
    for (std::size_t node = 0U; node < domain.nodes().size(); ++node) {
        const std::size_t offset = node * kDofsPerNode;
        std::array<double, 3> nodalAppliedForce{};
        std::array<double, 3> nodalReactionForce{};
        std::array<double, 3> nodalAppliedMoment{};
        std::array<double, 3> nodalReactionMoment{};
        for (std::size_t component = 0U; component < 3U; ++component) {
            nodalAppliedForce[component] = externalForce[offset + component];
            nodalAppliedMoment[component] =
                externalForce[offset + 3U + component];
            if (constrained[offset + component] != 0U) {
                nodalReactionForce[component] = residual[offset + component];
            }
            if (constrained[offset + 3U + component] != 0U) {
                nodalReactionMoment[component] =
                    residual[offset + 3U + component];
            }
        }

        const auto appliedForceMoment =
            cross(domain.nodes()[node].coordinates, nodalAppliedForce);
        const auto reactionForceMoment =
            cross(domain.nodes()[node].coordinates, nodalReactionForce);
        for (std::size_t component = 0U; component < 3U; ++component) {
            nodalAppliedMoment[component] += appliedForceMoment[component];
            nodalReactionMoment[component] += reactionForceMoment[component];
        }
        if (!accumulateVectorAndScale(
                appliedForce, appliedForceScale, nodalAppliedForce) ||
            !accumulateVectorAndScale(
                reactionForce, reactionForceScale, nodalReactionForce) ||
            !accumulateVectorAndScale(
                appliedMoment, appliedMomentScale, nodalAppliedMoment) ||
            !accumulateVectorAndScale(
                reactionMoment, reactionMomentScale, nodalReactionMoment)) {
            return recoveryFailure(
                "nonfinite-recovery-value",
                domain.nodes()[node].location,
                domain.nodes()[node].sourceId.sourceLabelText,
                "Global force and moment evidence must remain finite in source-node order.");
        }
    }

    std::array<double, 3> forceBalance{};
    std::array<double, 3> momentBalance{};
    for (std::size_t component = 0U; component < 3U; ++component) {
        forceBalance[component] =
            appliedForce[component] + reactionForce[component];
        momentBalance[component] =
            appliedMoment[component] + reactionMoment[component];
        candidate.equilibrium[component] = forceBalance[component];
        candidate.equilibrium[3U + component] = momentBalance[component];
    }
    const double forceMetric = normalizedBalance(
        forceBalance,
        (std::max)(appliedForceScale, reactionForceScale));
    const double momentMetric = normalizedBalance(
        momentBalance,
        (std::max)(appliedMomentScale, reactionMomentScale));
    candidate.verificationMetrics = {
        normalizedResidual, forceMetric, momentMetric};
    if (!finite(candidate.equilibrium) ||
        !finite(candidate.verificationMetrics)) {
        return recoveryFailure(
            "nonfinite-recovery-value",
            {domain.sourcePath(), 0U},
            "global-equilibrium",
            "Global equilibrium values and their physical normalization scales must be finite.");
    }
    if (forceMetric > kGlobalEquilibriumTolerance ||
        momentMetric > kGlobalEquilibriumTolerance) {
        return recoveryFailure(
            "global-equilibrium-tolerance-failure",
            {domain.sourcePath(), 0U},
            "global-equilibrium",
            "Normalized global force or moment balance exceeds 1e-10.");
    }
    return Status::ok();
}

std::optional<AxisSet> localAxes(const Domain& domain,
                                 const EulerBeam3DDefinition& element) {
    const auto& first = domain.nodes()[element.nodeIndices[0U]].coordinates;
    const auto& second = domain.nodes()[element.nodeIndices[1U]].coordinates;
    const std::array<double, 3> delta = {
        second[0U] - first[0U],
        second[1U] - first[1U],
        second[2U] - first[2U]};
    const double length = norm(delta);
    if (!std::isfinite(length) || !(length > 0.0)) {
        return std::nullopt;
    }
    const std::array<double, 3> ex = {
        delta[0U] / length, delta[1U] / length, delta[2U] / length};
    const auto& guide = domain.sections()[element.sectionIndex].firstAxis;
    const double projection = dot(guide, ex);
    const std::array<double, 3> eyTrial = {
        guide[0U] - projection * ex[0U],
        guide[1U] - projection * ex[1U],
        guide[2U] - projection * ex[2U]};
    const double eyNorm = norm(eyTrial);
    if (!std::isfinite(eyNorm) || !(eyNorm > 0.0)) {
        return std::nullopt;
    }
    const std::array<double, 3> ey = {
        eyTrial[0U] / eyNorm,
        eyTrial[1U] / eyNorm,
        eyTrial[2U] / eyNorm};
    const std::array<double, 3> ez = cross(ex, ey);
    const AxisSet axes = {ex, ey, ez};
    for (const auto& axis : axes) {
        for (const double component : axis) {
            if (!std::isfinite(component)) {
                return std::nullopt;
            }
        }
    }
    return axes;
}

bool sameAxes(const AxisSet& left, const AxisSet& right) {
    for (std::size_t axis = 0U; axis < left.size(); ++axis) {
        for (std::size_t component = 0U;
             component < left[axis].size();
             ++component) {
            if (std::abs(left[axis][component] - right[axis][component]) >
                kAxisTolerance) {
                return false;
            }
        }
    }
    return true;
}

} // namespace

Status ResultRecovery::recover(const AnalysisModel& model,
                               const DofManager& dofs,
                               const SparseMatrix& fullStiffness,
                               AnalysisState& state) {
    const Status inputStatus =
        validateRecoveryInputs(model, dofs, fullStiffness, state);
    if (!inputStatus.isOk()) {
        return inputStatus;
    }

    Vector internalForce = fullStiffness.multiply(state.displacement());
    if (!finite(internalForce)) {
        return recoveryFailure(
            "nonfinite-recovery-value",
            {model.domain().sourcePath(), 0U},
            model.domain().sourceContentIdentity(),
            "Full stiffness multiplication must produce finite internal force.");
    }
    Vector residual{dofs.fullDofCount()};
    for (std::size_t fullDof = 0U; fullDof < residual.size(); ++fullDof) {
        residual[fullDof] =
            internalForce[fullDof] - state.externalForce()[fullDof];
        if (!std::isfinite(residual[fullDof])) {
            return recoveryFailure(
                "nonfinite-recovery-value",
                {model.domain().sourcePath(), 0U},
                std::to_string(fullDof),
                "Internal-minus-external residual must remain finite.");
        }
    }

    const double residualNorm = indexedNorm(residual, dofs.freeDofs());
    const auto internalTermNorms = freeEquationInternalTermNorms(
        fullStiffness, state.displacement(), dofs);
    const double externalNorm =
        indexedNorm(state.externalForce(), dofs.freeDofs());
    // Normalize against the three terms of
    // Kff*df + Kfc*dc - Ff. Using only the already-cancelled K*d term would
    // classify prescribed-only equilibrium roundoff as a unit residual.
    const double denominator = (std::max)({
        internalTermNorms[0U], internalTermNorms[1U], externalNorm});
    if (!std::isfinite(residualNorm) || !std::isfinite(denominator)) {
        return recoveryFailure(
            "nonfinite-recovery-value",
            {model.domain().sourcePath(), 0U},
            "free-residual",
            "Free residual and its physical normalization scale must be finite.");
    }
    // An exact zero-load equilibrium is well-defined as zero. No unit floor is
    // introduced; a nonzero residual with zero physical scale fails closed.
    const double normalizedResidual = denominator == 0.0
        ? (residualNorm == 0.0
               ? 0.0
               : (std::numeric_limits<double>::infinity)())
        : residualNorm / denominator;
    if (!std::isfinite(normalizedResidual) ||
        normalizedResidual > kFreeResidualTolerance) {
        return recoveryFailure(
            "free-residual-tolerance-failure",
            {model.domain().sourcePath(), 0U},
            "free-residual",
            "The normalized free residual exceeds 1e-10.");
    }

    // The full-space reaction dataset preserves free residual evidence while
    // its constrained entries are the physical reactions from K*d-F. Element
    // end actions remain distinct output and are never re-summed here.
    Vector reaction = residual;

    std::vector<EndpointResultRow> endpointRows;
    std::vector<GaussResultRow> gaussRows;
    std::vector<StressS11Row> stressRows;
    endpointRows.reserve(model.activeElements().size() * 2U);
    gaussRows.reserve(model.activeElements().size() * 2U);
    const Domain& domain = model.domain();
    for (const EntityIndex elementIndex : model.activeElements()) {
        const auto& definition = domain.elements()[elementIndex];
        auto beam = EulerBeam3D::create(
            domain.nodes()[definition.nodeIndices[0U]],
            domain.nodes()[definition.nodeIndices[1U]],
            domain.sections()[definition.sectionIndex],
            domain.materials()[definition.materialIndex]);
        if (!beam.hasValue()) {
            return beam.status();
        }

        Vector elementDisplacement{kElementDofCount};
        const auto& scatter = dofs.elementScatter(elementIndex);
        for (std::size_t localDof = 0U;
             localDof < kElementDofCount;
             ++localDof) {
            elementDisplacement[localDof] =
                state.displacement()[scatter[localDof]];
        }
        const BeamRecovery recovered =
            beam.value().recover(elementDisplacement);
        for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) {
            if (!finite(recovered.equilibriumEndActions[endpoint]) ||
                !finite(recovered.endpointSectionResultants[endpoint])) {
                return recoveryFailure(
                    "nonfinite-recovery-value",
                    definition.location,
                    definition.sourceId.sourceLabelText,
                    "Endpoint recovery values must be finite.");
            }
            endpointRows.push_back({
                elementIndex,
                static_cast<int>(endpoint),
                domain.nodes()[definition.nodeIndices[endpoint]].sourceId,
                recovered.equilibriumEndActions[endpoint],
                recovered.endpointSectionResultants[endpoint]});
        }
        for (std::size_t gauss = 0U; gauss < 2U; ++gauss) {
            if (!finite(recovered.gaussGeneralizedStrains[gauss]) ||
                !finite(recovered.gaussGeneralizedResultants[gauss])) {
                return recoveryFailure(
                    "nonfinite-recovery-value",
                    definition.location,
                    definition.sourceId.sourceLabelText,
                    "Gauss recovery values must be finite.");
            }
            gaussRows.push_back({
                elementIndex,
                static_cast<int>(gauss + 1U),
                recovered.gaussGeneralizedStrains[gauss],
                recovered.gaussGeneralizedResultants[gauss]});
        }
        for (const auto& point : recovered.stressPoints) {
            if ((point.gaussPoint != 1 && point.gaussPoint != 2) ||
                !std::isfinite(point.x1) || !std::isfinite(point.x2) ||
                !std::isfinite(point.s11)) {
                return recoveryFailure(
                    "nonfinite-recovery-value",
                    definition.location,
                    definition.sourceId.sourceLabelText,
                    "Stress recovery identity and values must be finite and ordered.");
            }
            stressRows.push_back({
                elementIndex,
                point.gaussPoint,
                point.sectionPoint,
                point.x1,
                point.x2,
                point.s11,
                point.source});
        }
    }

    ShellStateCandidate shellCandidate{};
    std::vector<EntityIndex> expectedShellElements;
    if (!domain.shellElements().empty()) {
        if (domain.shellElements().size() >
            (std::numeric_limits<std::size_t>::max)() /
                kShellLocationCount) {
            return recoveryFailure(
                "invalid-recovery-dimensions",
                {domain.sourcePath(), 0U},
                domain.sourceContentIdentity(),
                "The shell result-row inventory exceeds the addressable range.");
        }
        std::vector<std::optional<std::array<double, 3>>> directorsByNode(
            domain.nodes().size());
        for (const auto& frame : domain.shellNodeInitialFrames()) {
            if (frame.nodeIndex >= directorsByNode.size() ||
                directorsByNode[frame.nodeIndex].has_value()) {
                return recoveryFailure(
                    "invalid-recovery-entity",
                    {domain.sourcePath(), 0U},
                    std::to_string(frame.nodeIndex),
                    "Shell initial directors must map uniquely to model nodes.");
            }
            directorsByNode[frame.nodeIndex] = frame.director;
        }

        shellCandidate.rows.reserve(
            domain.shellElements().size() * kShellLocationCount);
        expectedShellElements.reserve(domain.shellElements().size());
        constexpr std::array<ShellMidsurfaceLocation, kShellLocationCount>
            locations{
                ShellMidsurfaceLocation::gp1,
                ShellMidsurfaceLocation::gp2,
                ShellMidsurfaceLocation::gp3,
                ShellMidsurfaceLocation::gp4};
        constexpr std::array<ShellSectionPosition, 3> positions{
            ShellSectionPosition::bottom,
            ShellSectionPosition::middle,
            ShellSectionPosition::top};
        constexpr std::array<double, 3> zeta{-1.0, 0.0, 1.0};
        for (std::size_t elementOrder = 0U;
             elementOrder < domain.shellElements().size();
             ++elementOrder) {
            const EntityIndex elementIndex =
                static_cast<EntityIndex>(elementOrder);
            const auto& definition = domain.shellElements()[elementOrder];
            std::array<const Node*, 4> nodes{};
            std::array<std::array<double, 3>, 4> directors{};
            for (std::size_t nodePosition = 0U;
                 nodePosition < definition.nodeIndices.size();
                 ++nodePosition) {
                const EntityIndex node = definition.nodeIndices[nodePosition];
                if (!directorsByNode[node].has_value()) {
                    return recoveryFailure(
                        "invalid-recovery-entity",
                        definition.location,
                        definition.sourceId.sourceLabelText,
                        "Shell recovery requires one initial director per element node.");
                }
                nodes[nodePosition] = &domain.nodes()[node];
                directors[nodePosition] = *directorsByNode[node];
            }

            auto shell = Mitc4Shell::create(
                nodes,
                directors,
                domain.shellSections()[definition.sectionIndex],
                domain.materials()[definition.materialIndex]);
            if (!shell.hasValue()) {
                return shell.status();
            }
            Vector elementDisplacement{kShellElementDofCount};
            const auto& scatter = dofs.shellElementScatter(elementIndex);
            for (std::size_t localDof = 0U;
                 localDof < kShellElementDofCount;
                 ++localDof) {
                elementDisplacement[localDof] =
                    state.displacement()[scatter[localDof]];
            }
            auto recovered = shell.value().recoverPhysical(
                elementDisplacement);
            if (!recovered.hasValue()) {
                return recovered.status();
            }
            const double accumulatedEnergy =
                shellCandidate.physicalStrainEnergy +
                recovered.value().strainEnergy;
            if (!std::isfinite(accumulatedEnergy)) {
                return recoveryFailure(
                    "nonfinite-recovery-value",
                    definition.location,
                    definition.sourceId.sourceLabelText,
                    "Source-order physical shell energy reduction must remain finite.");
            }
            shellCandidate.physicalStrainEnergy = accumulatedEnergy;
            expectedShellElements.push_back(elementIndex);

            for (std::size_t point = 0U;
                 point < recovered.value().points.size();
                 ++point) {
                const auto& physicalPoint = recovered.value().points[point];
                ShellResultRow row{};
                row.element = elementIndex;
                row.location = locations[point];
                row.naturalCoordinates = physicalPoint.naturalCoordinates;
                row.localFrame = {
                    physicalPoint.localFrame.e1,
                    physicalPoint.localFrame.e2,
                    physicalPoint.localFrame.e3};
                row.generalizedStrain = physicalPoint.generalizedStrain;
                row.sectionResultant = physicalPoint.sectionResultant;
                for (std::size_t position = 0U;
                     position < positions.size();
                     ++position) {
                    row.stress[position] = {
                        positions[position],
                        zeta[position],
                        physicalPoint.inPlaneStress[position]};
                }
                shellCandidate.rows.push_back(std::move(row));
            }
        }
        const Status evidenceStatus = populateShellGlobalEvidence(
            domain,
            dofs,
            state.externalForce(),
            residual,
            normalizedResidual,
            shellCandidate);
        if (!evidenceStatus.isOk()) {
            return evidenceStatus;
        }
    }

    // Build and validate a complete candidate state first. This preserves the
    // prior full residual, beam rows, and shell rows if any later shell or
    // candidate-inventory validation fails.
    AnalysisState candidateState = state;
    candidateState.internalForce() = std::move(internalForce);
    candidateState.residual() = std::move(residual);
    candidateState.reaction() = std::move(reaction);
    candidateState.endpointResults() = std::move(endpointRows);
    candidateState.gaussResults() = std::move(gaussRows);
    candidateState.stressResults() = std::move(stressRows);
    const Status shellCommitStatus = candidateState.commitShellResults(
        expectedShellElements, std::move(shellCandidate));
    if (!shellCommitStatus.isOk()) {
        return shellCommitStatus;
    }
    state = std::move(candidateState);
    return Status::ok();
}

Result<std::vector<NodeStationResultRow>>
ResultRecovery::normalizeSectionResultantsToNodeStations(
    const AnalysisModel& model,
    const std::vector<EndpointResultRow>& endpointRows,
    const std::array<double, 4>& componentTolerances) {
    const Domain& domain = model.domain();
    for (const double tolerance : componentTolerances) {
        if (!std::isfinite(tolerance) || tolerance < 0.0) {
            return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                "invalid-node-station-tolerance",
                {domain.sourcePath(), 0U},
                "component-tolerances",
                "Node-station component tolerances must be finite and nonnegative.");
        }
    }
    if (model.activeElements().size() >
            (std::numeric_limits<std::size_t>::max)() / 2U ||
        endpointRows.size() != model.activeElements().size() * 2U) {
        return recoveryResultFailure<std::vector<NodeStationResultRow>>(
            "invalid-node-station-shape",
            {domain.sourcePath(), 0U},
            std::to_string(endpointRows.size()),
            "Endpoint rows must contain exactly two rows per active element.");
    }

    std::vector<std::vector<const EndpointResultRow*>> rowsByNode(
        domain.nodes().size());
    for (std::size_t order = 0U;
         order < model.activeElements().size();
         ++order) {
        const EntityIndex elementIndex = model.activeElements()[order];
        if (elementIndex >= domain.elements().size()) {
            return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                "invalid-node-station-entity",
                {domain.sourcePath(), 0U},
                std::to_string(elementIndex),
                "Every active station element must be a valid stable entity.");
        }
        const auto& definition = domain.elements()[elementIndex];
        for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) {
            const auto& row = endpointRows[order * 2U + endpoint];
            const EntityIndex nodeIndex = definition.nodeIndices[endpoint];
            if (nodeIndex >= domain.nodes().size() ||
                row.element != elementIndex ||
                row.endpoint != static_cast<int>(endpoint) ||
                !sameSourceIdentity(row.node, domain.nodes()[nodeIndex].sourceId)) {
                return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                    "invalid-node-station-entity",
                    definition.location,
                    definition.sourceId.sourceLabelText,
                    "Endpoint rows must preserve active element, endpoint, and source-node order.");
            }
            if (!finite(row.sectionResultant)) {
                return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                    "nonfinite-node-station-value",
                    definition.location,
                    definition.sourceId.sourceLabelText,
                    "Node-station section resultants must be finite.");
            }
            rowsByNode[nodeIndex].push_back(&row);
        }
    }

    std::vector<unsigned char> loadedNodes(domain.nodes().size(), 0U);
    if (model.activeLoads().size() != model.step().loads.size()) {
        return recoveryResultFailure<std::vector<NodeStationResultRow>>(
            "invalid-node-station-entity",
            model.step().location,
            model.step().name,
            "The active load view must preserve every sole-step load.");
    }
    for (std::size_t order = 0U; order < model.activeLoads().size(); ++order) {
        const EntityIndex loadIndex = model.activeLoads()[order];
        if (loadIndex != order || loadIndex >= model.step().loads.size()) {
            return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                "invalid-node-station-entity",
                model.step().location,
                std::to_string(loadIndex),
                "Active loads must remain in stable source order.");
        }
        const auto& load = model.step().loads[loadIndex];
        if (!std::isfinite(load.magnitude)) {
            return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                "nonfinite-node-station-value",
                load.location,
                load.target,
                "Station eligibility requires finite concentrated loads.");
        }
        auto targets = resolveLoadTarget(domain, load);
        if (!targets.hasValue()) {
            return Result<std::vector<NodeStationResultRow>>::failure(
                targets.status());
        }
        if (load.magnitude != 0.0) {
            for (const EntityIndex node : targets.value()) {
                loadedNodes[node] = 1U;
            }
        }
    }

    std::vector<NodeStationResultRow> stations;
    stations.reserve(domain.nodes().size());
    for (std::size_t nodeIndex = 0U;
         nodeIndex < rowsByNode.size();
         ++nodeIndex) {
        const auto& incident = rowsByNode[nodeIndex];
        if (incident.empty()) {
            continue;
        }
        if (incident.size() == 1U) {
            stations.push_back({
                domain.nodes()[nodeIndex].sourceId,
                incident.front()->element,
                incident.front()->sectionResultant});
            continue;
        }
        if (incident.size() != 2U || loadedNodes[nodeIndex] != 0U) {
            return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                "ineligible-node-station",
                domain.nodes()[nodeIndex].location,
                domain.nodes()[nodeIndex].sourceId.sourceLabelText,
                "Interior station collapse requires exactly two unloaded endpoints.");
        }

        const auto& firstElement = domain.elements()[incident[0U]->element];
        const auto& secondElement = domain.elements()[incident[1U]->element];
        const bool chainOrientation =
            incident[0U]->endpoint != incident[1U]->endpoint &&
            ((incident[0U]->endpoint == 1 && incident[1U]->endpoint == 0) ||
             (incident[0U]->endpoint == 0 && incident[1U]->endpoint == 1));
        const auto firstAxes = localAxes(domain, firstElement);
        const auto secondAxes = localAxes(domain, secondElement);
        if (!chainOrientation ||
            firstElement.sectionIndex != secondElement.sectionIndex ||
            !firstAxes.has_value() || !secondAxes.has_value() ||
            !sameAxes(*firstAxes, *secondAxes)) {
            return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                "ineligible-node-station",
                domain.nodes()[nodeIndex].location,
                domain.nodes()[nodeIndex].sourceId.sourceLabelText,
                "Interior station endpoints require one consistent section and local-axis chain.");
        }

        // Endpoint sectionResultant rows already use the positive-local-x cut
        // convention. Once common orientation is proven, no outward-action
        // endpoint sign is applied and the values are directly comparable.
        for (std::size_t component = 0U;
             component < componentTolerances.size();
             ++component) {
            const double difference = std::abs(
                incident[0U]->sectionResultant[component] -
                incident[1U]->sectionResultant[component]);
            if (!std::isfinite(difference)) {
                return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                    "nonfinite-node-station-value",
                    domain.nodes()[nodeIndex].location,
                    domain.nodes()[nodeIndex].sourceId.sourceLabelText,
                    "Endpoint comparison must produce a finite difference.");
            }
            if (difference > componentTolerances[component]) {
                return recoveryResultFailure<std::vector<NodeStationResultRow>>(
                    "node-station-tolerance-failure",
                    domain.nodes()[nodeIndex].location,
                    domain.nodes()[nodeIndex].sourceId.sourceLabelText,
                    "Interior endpoint resultants disagree beyond component tolerance.");
            }
        }

        const EndpointResultRow* representative =
            incident[0U]->element < incident[1U]->element
                ? incident[0U]
                : incident[1U];
        stations.push_back({
            domain.nodes()[nodeIndex].sourceId,
            representative->element,
            representative->sectionResultant});
    }
    return Result<std::vector<NodeStationResultRow>>::success(
        std::move(stations));
}

} // namespace fesa
