#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>
#include <pqxx/connection.hxx>
#include <pqxx/transaction.hxx>
#include "json.hpp"
#include <fstream>

template<class T>
using o = boost::optional<T>;
using namespace std;
using json = nlohmann::json;

class missing_key : public runtime_error {
public:
    missing_key(const json::string_t key) : runtime_error("Missing key: " + key), key(key) {
    }

    const json::string_t key;
};

template<typename T>
T get(json j, string key) {
    auto ref = j[key];

    if (ref.is_null()) {
        throw missing_key(key);
    }

    return ref;
}

int main(int argc, char *argv[]) {
    cout << "reading from " << argv[1] << endl;

    fstream f(argv[1]);

    json j;

    j << f;

    pqxx::connection c("host=localhost dbname=soil-moisture");

    pqxx::work work(c);

    string mac = j["mac"]; // "aa:bb:cc:dd:ee:ff";

    auto rs = work.parameterized("select id from soil_moisture_device where mac=$1")(mac).exec();

    if (!rs.size()) {
        cout << "New device: " << mac << endl;

        rs = work.parameterized("insert into soil_moisture_device(mac) values($1) returning id")(mac).exec();
    }

    auto deviceId = rs.begin()["id"].as<int>();

    int sensor = j["sensor"];

    rs = work.parameterized("select id from soil_moisture_sensor where device=$1 and sensor=$2")(deviceId)(sensor).exec();

    if (!rs.size()) {
        cout << "New sensor: " << sensor << endl;

        rs = work.parameterized("insert into soil_moisture_sensor(device, sensor) values($1, $2) returning id")(deviceId)(sensor).exec();
    }
    auto sensorId = rs.begin()["id"].as<int>();

    unsigned long timestamp = get<unsigned long>(j, "timestamp");
    unsigned int value = get<unsigned int>(j, "value");

    work.parameterized("insert into soil_moisture_sample(sensor, timestamp, value) values($1, $2, $3)")(sensorId)(timestamp)(value).exec();

    cout << "Sample inserted" << endl;

    work.commit();

    return EXIT_SUCCESS;
}