#include "AppController.h"
#include <json/json.h>

drogon::Task<HttpResponsePtr> AppController::apiTrack(HttpRequestPtr req) {
    auto jsonPtr = req->getJsonObject();
    if (!jsonPtr) {
        auto resp = HttpResponse::newHttpResponse();
        resp->setStatusCode(k400BadRequest);
        co_return resp;
    }
    std::string serial = (*jsonPtr)["serial_number"].asString();
    std::string sector = (*jsonPtr)["sector_id"].asString();
    co_await dbService_.insert_sql("Trackers", {"888", serial, sector, "active", "0", "0"});
    
    Json::Value response;
    response["status"] = "tracker_data_received";
    response["source"] = "api_tunnel";
    co_return HttpResponse::newHttpJsonResponse(response);
}

drogon::Task<HttpResponsePtr> AppController::apiLogin(HttpRequestPtr req) {
    auto jsonPtr = req->getJsonObject();
    if (!jsonPtr) {
        Json::Value err;
        err["status"] = "error";
        err["message"] = "Invalid JSON payload. Expected {\"userId\": \"...\", \"password\": \"...\"}";
        auto resp = HttpResponse::newHttpJsonResponse(err);
        resp->setStatusCode(k400BadRequest);
        co_return resp;
    }

    std::string userId = (*jsonPtr)["userId"].asString();
    std::string password = (*jsonPtr)["password"].asString();

    auto res = co_await dbService_.select_sql("Agents",
        {"login", "password_hash", "first_name", "last_name", "role", "sector_id"},
        {"0", userId, "0", "0", "0", "0", "0", "0", "0", "0"});

    bool success = false;
    std::string name = "";
    std::string role = "agent";
    std::string sectorId = "";

    for(auto const& row : res) {
        if(row["password_hash"].as<std::string>() == password) {
            success = true;
            name = row["first_name"].as<std::string>() + " " + row["last_name"].as<std::string>();
            role = row["role"].as<std::string>();
            sectorId = row["sector_id"].isNull() ? "" : row["sector_id"].as<std::string>();
            break;
        }
    }

    Json::Value response;
    if (success) {
        auto session = req->getSession();
        session->insert("logged_in", true);
        session->insert("userId", userId);
        session->insert("userName", name);
        session->insert("role", role);
        session->insert("sector_id", sectorId);

        response["status"] = "success";
        response["role"] = role;
        response["sectorId"] = sectorId;
        response["userName"] = name;
        co_return HttpResponse::newHttpJsonResponse(response);
    }

    response["status"] = "error";
    response["message"] = "Invalid ID or Password";
    auto resp = HttpResponse::newHttpJsonResponse(response);
    resp->setStatusCode(k401Unauthorized);
    co_return resp;
}

drogon::Task<HttpResponsePtr> AppController::apiStatus(HttpRequestPtr req) {
    auto session = req->getSession();
    bool loggedIn = session->getOptional<bool>("logged_in").value_or(false);

    Json::Value response;
    if (loggedIn) {
        response["status"] = "success";
        response["role"] = session->getOptional<std::string>("role").value_or("agent");
        response["sectorId"] = session->getOptional<std::string>("sector_id").value_or("");
        response["userName"] = session->getOptional<std::string>("userName").value_or("");
        co_return HttpResponse::newHttpJsonResponse(response);
    }
    
    response["status"] = "error";
    response["message"] = "Not authenticated";
    auto resp = HttpResponse::newHttpJsonResponse(response);
    resp->setStatusCode(k401Unauthorized);
    co_return resp;
}

drogon::Task<HttpResponsePtr> AppController::apiLogout(HttpRequestPtr req) {
    auto session = req->getSession();
    session->erase("logged_in");
    session->erase("userId");
    session->erase("userName");
    session->erase("role");
    session->erase("sector_id");

    Json::Value response;
    response["status"] = "success";
    response["message"] = "Logged out successfully";
    
    // Clear cookies explicitly if needed, but Drogon's session erase usually handles tracking properly.
    auto resp = HttpResponse::newHttpJsonResponse(response);
    co_return resp;
}

drogon::Task<HttpResponsePtr> AppController::apiTrackers(HttpRequestPtr req) {
    auto session = req->getSession();
    if (!session->getOptional<bool>("logged_in").value_or(false)) {
        Json::Value err;
        err["status"] = "error";
        err["message"] = "Not authenticated";
        auto resp = HttpResponse::newHttpJsonResponse(err);
        resp->setStatusCode(k401Unauthorized);
        co_return resp;
    }

    std::string role = session->getOptional<std::string>("role").value_or("agent");
    std::string agentSectorId = session->getOptional<std::string>("sector_id").value_or("");

    auto res = co_await dbService_.select_sql("Trackers", {}, {});

    auto dbClient = drogon::app().getDbClient();
    auto pointsRes = co_await dbClient->execSqlCoro("SELECT tracker_id, latitude, longitude, altitude, timestamp FROM Point_History ORDER BY timestamp ASC");

    Json::Value pointsJson(Json::objectValue);
    for(auto const& row : pointsRes) {
        std::string tid = row["tracker_id"].as<std::string>();
        if(!pointsJson.isMember(tid)) {
            pointsJson[tid] = Json::arrayValue;
        }
        Json::Value pt(Json::objectValue);
        pt["lat"] = row["latitude"].as<double>();
        pt["lng"] = row["longitude"].as<double>();
        if (!row["altitude"].isNull()) {
            pt["alt"] = row["altitude"].as<double>();
        }
        pt["timestamp"] = row["timestamp"].as<std::string>();
        pointsJson[tid].append(pt);
    }

    Json::Value trackersArr(Json::arrayValue);
    for(auto const& row : res) {
        std::string trkSectorId = row["sector_id"].isNull() ? "" : row["sector_id"].as<std::string>();
        
        // Agent sector filtering
        if(role == "agent" && trkSectorId != agentSectorId) {
            continue;
        }

        Json::Value t(Json::objectValue);
        std::string tid = row["tracker_id"].as<std::string>();
        t["trackerId"] = tid;
        t["serialNumber"] = row["serial_number"].as<std::string>();
        t["sectorId"] = trkSectorId;
        t["status"] = row["status"].as<std::string>();
        t["rentalStart"] = row["rental_start"].isNull() ? "" : row["rental_start"].as<std::string>();
        t["rentalEnd"] = row["rental_end"].isNull() ? "" : row["rental_end"].as<std::string>();
        
        t["history"] = pointsJson.isMember(tid) ? pointsJson[tid] : Json::arrayValue;

        trackersArr.append(t);
    }

    Json::Value response;
    response["status"] = "success";
    response["data"] = trackersArr;
    co_return HttpResponse::newHttpJsonResponse(response);
}


drogon::Task<HttpResponsePtr> AppController::apiSigFoxData(HttpRequestPtr req) {
    auto jsonPtr = req->getJsonObject();
    if (!jsonPtr) {
        Json::Value err;
        err["status"] = "error";
        err["message"] = "Invalid JSON payload. Expected {\"data\": \"...\", \"deviceid\": \"...\"}";
        auto resp = HttpResponse::newHttpJsonResponse(err);
        resp->setStatusCode(k400BadRequest);
        co_return resp;
    }

    std::string deviceId = (*jsonPtr)["deviceid"].asString();
    std::string data = (*jsonPtr)["data"].asString();

    std::string userId = (*jsonPtr)["userId"].asString();
    std::string password = (*jsonPtr)["password"].asString();

    auto res = co_await dbService_.select_sql("Agents",
        {"login", "password_hash", "first_name", "last_name", "role", "sector_id"},
        {"0", userId, "0", "0", "0", "0", "0", "0", "0", "0"});

    bool success = false;
    std::string name = "";
    std::string role = "agent";
    std::string sectorId = "";

    for(auto const& row : res) {
        if(row["password_hash"].as<std::string>() == password) {
            success = true;
            name = row["first_name"].as<std::string>() + " " + row["last_name"].as<std::string>();
            role = row["role"].as<std::string>();
            sectorId = row["sector_id"].isNull() ? "" : row["sector_id"].as<std::string>();
            break;
        }
    }

    Json::Value response;
    if (success) {
        response["status"] = "success";
        response["data"] = data;
        response["deviceId"] = deviceId;

        co_return HttpResponse::newHttpJsonResponse(response);
    }

    response["status"] = "error";
    response["message"] = "Invalid ID or Password";
    auto resp = HttpResponse::newHttpJsonResponse(response);
    resp->setStatusCode(k401Unauthorized);
    co_return resp;
}