#include "ConfigLoader.h"

#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include <QPoint>

#include "toml.hpp"

#include "Rotation.h"
#include "ShipLayout.h"
#include "TomlHelpers.h"

namespace
{

Rotation parseRotationString(const std::string& s)
{
    if (s == "east")  { return Rotation::East;  }
    if (s == "south") { return Rotation::South; }
    if (s == "west")  { return Rotation::West;  }
    return Rotation::North;
}

std::vector<PlacedModule> parsePlacedModules(const toml::array& arr,
                                             const std::string& file,
                                             const std::string& path)
{
    std::vector<PlacedModule> result;
    result.reserve(arr.size());
    for (std::size_t i = 0; i < arr.size(); ++i)
    {
        const std::string elemPath = path + "[" + std::to_string(i) + "]";
        const toml::table* t = arr[i].as_table();
        if (t == nullptr) { continue; }
        toml::table& mt = const_cast<toml::table&>(*t);

        const std::optional<std::string> type = mt["type"].value<std::string>();
        const std::optional<int64_t>     x    = mt["x"].value<int64_t>();
        const std::optional<int64_t>     y    = mt["y"].value<int64_t>();
        const std::optional<std::string> rot  = mt["rotation"].value<std::string>();
        if (!type || !x || !y || !rot) { continue; }

        PlacedModule pm;
        pm.moduleId = *type;
        pm.position = QPoint(static_cast<int>(*x), static_cast<int>(*y));
        pm.rotation = parseRotationString(*rot);
        result.push_back(std::move(pm));
    }
    return result;
}

}  // namespace

ShipsConfig ConfigLoader::loadShips(const std::string& path)
{
    const std::string file = "ships.toml";
    toml::table tbl = utility::parseFile(path, file);

    ShipsConfig cfg;
    const toml::array& arr = utility::requireArray(tbl["ship"], file, "ship");

    for (std::size_t i = 0; i < arr.size(); ++i)
    {
        const std::string elemPath = "ship[" + std::to_string(i) + "]";
        const toml::table* st = arr[i].as_table();
        if (st == nullptr)
        {
            throw utility::makeError(file, elemPath, "not a table");
        }
        toml::table& mt = const_cast<toml::table&>(*st);

        ShipDef def;
        def.id                 = utility::requireString(mt["id"],                 file, elemPath + ".id");
        def.layout             = utility::requireStringArray(mt["layout"],       file, elemPath + ".layout");

        // Schematic
        {
            const std::string bpPath = elemPath + ".schematic";
            const toml::table& bpTable = utility::requireTable(mt["schematic"], file, bpPath);
            toml::table& bpMt = const_cast<toml::table&>(bpTable);

            const toml::array& materials = utility::requireArray(bpMt["materials"], file, bpPath + ".materials");
            def.schematic.materials = utility::parseIngredients(materials, file, bpPath + ".materials");
            def.schematic.productionTimeSeconds = utility::requireDouble(
                bpMt["production_time_seconds"], file, bpPath + ".production_time_seconds");
        }

        // Health
        {
            const std::string hPath = elemPath + ".health";
            const toml::table& hTable = utility::requireTable(mt["health"], file, hPath);
            toml::table& hMt = const_cast<toml::table&>(hTable);
            def.health.hp = static_cast<float>(utility::requireDouble(hMt["hp"], file, hPath + ".hp"));
        }

        // Movement
        {
            const std::string mPath = elemPath + ".movement";
            const toml::table& mTable = utility::requireTable(mt["movement"], file, mPath);
            toml::table& mMt = const_cast<toml::table&>(mTable);
            def.movement.speed_mps                    = static_cast<float>(utility::requireDouble(mMt["speed_mps"],                    file, mPath + ".speed_mps"));
            def.movement.mainAcceleration_mpss        = static_cast<float>(utility::requireDouble(mMt["main_acceleration_mpss"],        file, mPath + ".main_acceleration_mpss"));
            def.movement.maneuveringAcceleration_mpss = static_cast<float>(utility::requireDouble(mMt["maneuvering_acceleration_mpss"], file, mPath + ".maneuvering_acceleration_mpss"));
            def.movement.angularAcceleration_radpss   = static_cast<float>(utility::requireDouble(mMt["angular_acceleration_radpss"],   file, mPath + ".angular_acceleration_radpss"));
            def.movement.maxRotationSpeed_radps       = static_cast<float>(utility::requireDouble(mMt["max_rotation_speed_radps"],       file, mPath + ".max_rotation_speed_radps"));
        }

        // Sensor
        {
            const std::string snsPath = elemPath + ".sensor";
            const toml::table& snsTable = utility::requireTable(mt["sensor"], file, snsPath);
            toml::table& snsMt = const_cast<toml::table&>(snsTable);
            def.sensor.sensorRange_m = static_cast<float>(utility::requireDouble(snsMt["sensor_range_m"], file, snsPath + ".sensor_range_m"));
        }

        // Optional: default_modules (REQ-WAV-DEFAULT-MODULES)
        if (mt.contains("default_modules"))
        {
            const toml::array& modArr = utility::requireArray(mt["default_modules"], file,
                elemPath + ".default_modules");
            def.defaultModules = parsePlacedModules(modArr, file,
                elemPath + ".default_modules");
        }

        cfg.ships.push_back(std::move(def));
    }

    return cfg;
}
