#include "fesa/io/abaqus/input_reader.hpp"

#include <gtest/gtest.h>

#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>

namespace {

class TemporaryInputFile {
public:
    TemporaryInputFile(const std::string& stem, const std::string& content)
        : path_{std::filesystem::temp_directory_path() /
                ("fesa-" + stem + ".inp")} {
        std::ofstream stream{path_, std::ios::binary | std::ios::trunc};
        stream.write(content.data(), static_cast<std::streamsize>(content.size()));
        if (!stream) {
            throw std::runtime_error{"Unable to create INP syntax test fixture."};
        }
    }

    ~TemporaryInputFile() {
        std::error_code error;
        std::filesystem::remove(path_, error);
    }

    const std::filesystem::path& path() const noexcept {
        return path_;
    }

private:
    std::filesystem::path path_;
};

} // namespace

TEST(InpSyntax, CanonicalizesKeywordAndParameterNamesOnly) {
    const std::string originalLine =
        "  *eLeMeNt, TyPe= b33 , generate, ELSET=Beam_Set  ";
    const TemporaryInputFile input{
        "canonical-names", originalLine + "\n"};

    const auto result = fesa::AbaqusInputReader{}.read(input.path());

    ASSERT_TRUE(result.hasValue());
    ASSERT_EQ(result.value().blocks.size(), 1U);
    const auto& block = result.value().blocks[0];
    EXPECT_EQ(block.canonicalName, "ELEMENT");
    EXPECT_EQ(block.originalLine, originalLine);
    EXPECT_EQ(block.location.line, 1U);

    ASSERT_EQ(block.parameters.size(), 3U);
    EXPECT_EQ(block.parameters[0].name, "TYPE");
    ASSERT_TRUE(block.parameters[0].value.has_value());
    EXPECT_EQ(*block.parameters[0].value, "b33");
    EXPECT_EQ(block.parameters[1].name, "GENERATE");
    EXPECT_FALSE(block.parameters[1].value.has_value());
    EXPECT_EQ(block.parameters[2].name, "ELSET");
    ASSERT_TRUE(block.parameters[2].value.has_value());
    EXPECT_EQ(*block.parameters[2].value, "Beam_Set");
}

TEST(InpSyntax, PreservesDataAndSourceLocations) {
    const std::string exactBytes =
        "** retained only in line accounting\r\n"
        "\r\n"
        "*NoDe\r\n"
        "  0007, Label_A, ,\r\n";
    const TemporaryInputFile input{"data-and-locations", exactBytes};

    const auto result = fesa::AbaqusInputReader{}.read(input.path());

    ASSERT_TRUE(result.hasValue());
    EXPECT_EQ(
        result.value().sourcePath,
        std::filesystem::absolute(input.path()).lexically_normal());
    EXPECT_EQ(result.value().sourceContentIdentity,
              "fnv1a64:c120b6ed2445be46");
    ASSERT_EQ(result.value().blocks.size(), 1U);
    const auto& block = result.value().blocks[0];
    EXPECT_EQ(block.canonicalName, "NODE");
    EXPECT_EQ(block.originalLine, "*NoDe");
    EXPECT_EQ(block.location.file, result.value().sourcePath);
    EXPECT_EQ(block.location.line, 3U);

    ASSERT_EQ(block.data.size(), 1U);
    EXPECT_EQ(
        block.data[0].fields,
        (std::vector<std::string>{"0007", "Label_A", "", ""}));
    EXPECT_EQ(block.data[0].location.file, result.value().sourcePath);
    EXPECT_EQ(block.data[0].location.line, 4U);
}
