#pragma once

#include "fesa/core/status.hpp"
#include "fesa/math/matrix.hpp"
#include "fesa/math/vector.hpp"
#include "fesa/model/model_types.hpp"

#include <array>
#include <string>

namespace fesa {

struct Mitc4ShapeFunctions {
    std::array<double, 4> values;
    std::array<double, 4> xiDerivatives;
    std::array<double, 4> etaDerivatives;
};

struct Mitc4LocalFrame {
    std::array<double, 3> e1;
    std::array<double, 3> e2;
    std::array<double, 3> e3;
};

struct Mitc4TyingWeights {
    std::array<double, 2> xiZeta;
    std::array<double, 2> etaZeta;
};

struct Mitc4QuadraturePoint {
    std::array<double, 3> naturalCoordinates;
    double weight;
};

struct Mitc4Stiffness {
    Matrix physicalLocal20;
    Matrix physicalGlobal24;
    Matrix drillingGlobal24;
    Matrix stabilizedGlobal24;
    double drillingStiffness;
};

struct Mitc4PhysicalRecoveryPoint {
    std::array<double, 2> naturalCoordinates;
    Mitc4LocalFrame localFrame;
    std::array<double, 8> generalizedStrain;
    std::array<double, 8> sectionResultant;
    std::array<std::array<double, 3>, 3> inPlaneStress;
};

struct Mitc4PhysicalRecovery {
    std::array<Mitc4PhysicalRecoveryPoint, 4> points;
    double strainEnergy;
};

// Concrete small-rotation MITC4 kinematics, constitutive, stiffness, and
// physical-only recovery kernel. Global equation/result ownership remains outside.
class Mitc4Shell {
public:
    static Result<Mitc4Shell> create(
        std::array<const Node*, 4> nodes,
        std::array<std::array<double, 3>, 4> initialDirectors,
        const ShellSection& section,
        const LinearElasticMaterial& material);

    static Mitc4ShapeFunctions shapeFunctions(double xi, double eta) noexcept;
    static Mitc4TyingWeights tyingWeights(double xi, double eta) noexcept;
    static const std::array<Mitc4QuadraturePoint, 8>&
    volumeQuadrature() noexcept;

    [[nodiscard]] Mitc4LocalFrame localFrame(double xi, double eta) const;
    [[nodiscard]] Matrix physicalTransformation20() const;
    [[nodiscard]] Matrix drillingTransformation4() const;
    [[nodiscard]] Matrix directStrainDisplacement20(
        double xi,
        double eta,
        double zeta) const;
    [[nodiscard]] Matrix covariantTyingShearSamples20() const;
    [[nodiscard]] Matrix strainDisplacement20(
        double xi,
        double eta,
        double zeta) const;

    [[nodiscard]] Matrix planeStressConstitutive() const;
    [[nodiscard]] Matrix materialConstitutive5() const;
    [[nodiscard]] Matrix membraneSectionMatrix() const;
    [[nodiscard]] Matrix bendingSectionMatrix() const;
    [[nodiscard]] Matrix transverseShearSectionMatrix() const;
    [[nodiscard]] Result<Mitc4Stiffness> stiffness() const;
    [[nodiscard]] Result<Mitc4PhysicalRecovery> recoverPhysical(
        const Vector& globalElementDisplacement24) const;

private:
    using Vector3 = std::array<double, 3>;

    struct GeometryData {
        std::array<Vector3, 3> covariant;
        std::array<Vector3, 3> reciprocal;
        Mitc4LocalFrame frame;
        double jacobian;
    };

    Mitc4Shell(
        std::array<Vector3, 4> coordinates,
        std::array<Vector3, 4> directors,
        std::array<Vector3, 4> tangentA,
        std::array<Vector3, 4> tangentB,
        Vector3 normalCandidate,
        double thickness,
        double youngsModulus,
        double poissonRatio,
        SourceLocation sourceLocation,
        std::string identity);

    bool evaluateGeometry(
        double xi,
        double eta,
        double zeta,
        GeometryData& result) const noexcept;
    std::array<std::array<Vector3, 3>, 20> basisDerivatives(
        double xi,
        double eta,
        double zeta) const noexcept;
    Matrix strainDisplacement(
        double xi,
        double eta,
        double zeta,
        const Matrix* tyingSamples) const;

    std::array<Vector3, 4> coordinates_;
    std::array<Vector3, 4> directors_;
    std::array<Vector3, 4> tangentA_;
    std::array<Vector3, 4> tangentB_;
    Vector3 normalCandidate_;
    double thickness_;
    double youngsModulus_;
    double poissonRatio_;
    SourceLocation sourceLocation_;
    std::string identity_;
};

} // namespace fesa
