#include "WebController.h"
#include <json/json.h>
bool WebController::checkSession(const HttpRequestPtr& req) {
    auto session = req->getSession();
    return session->getOptional<bool>("logged_in").value_or(false);
}
bool WebController::isAdmin(const HttpRequestPtr& req) {
    auto session = req->getSession();
    return session->getOptional<std::string>("role").value_or("agent") == "admin";
}
drogon::Task<HttpResponsePtr> WebController::loginView(HttpRequestPtr req) {
    HttpViewData data;
    data.insert("error", std::string(""));
    co_return HttpResponse::newHttpViewResponse("login_user_id.csp", data);
}
drogon::Task<HttpResponsePtr> WebController::loginSubmit(HttpRequestPtr req) {
    auto userId = req->getParameter("userId");
    auto password = req->getParameter("password");
    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;
        }
    }
    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);
        co_return HttpResponse::newRedirectionResponse("/");
    }
    HttpViewData data;
    data.insert("error", std::string("Invalid ID or Password"));
    co_return HttpResponse::newHttpViewResponse("login_user_id.csp", data);
}
drogon::Task<HttpResponsePtr> WebController::logout(HttpRequestPtr req) {
    req->getSession()->erase("logged_in");
    req->getSession()->erase("userName");
    req->getSession()->erase("role");
    req->getSession()->erase("sector_id");
    co_return HttpResponse::newRedirectionResponse("/login");
}
drogon::Task<HttpResponsePtr> WebController::homeView(HttpRequestPtr req) {
    if(!checkSession(req)) co_return HttpResponse::newRedirectionResponse("/login");
    HttpViewData data;
    data.insert("userName", req->getSession()->get<std::string>("userName"));
    data.insert("role", req->getSession()->getOptional<std::string>("role").value_or("agent"));
    co_return HttpResponse::newHttpViewResponse("home_expanded.csp", data);
}
drogon::Task<HttpResponsePtr> WebController::agentsList(HttpRequestPtr req) {
    if(!checkSession(req)) co_return HttpResponse::newRedirectionResponse("/login");
    if(!isAdmin(req)) co_return HttpResponse::newRedirectionResponse("/trackers");
    auto res = co_await dbService_.select_sql("Agents", {}, {});
    HttpViewData data;
    std::vector<std::map<std::string, std::string>> agents;
    for(auto const& row : res) {
        std::map<std::string, std::string> a;
        a["agent_id"] = row["agent_id"].as<std::string>();
        a["login"] = row["login"].as<std::string>();
        a["first_name"] = row["first_name"].as<std::string>();
        a["last_name"] = row["last_name"].as<std::string>();
        a["sector_id"] = row["sector_id"].as<std::string>();
        a["last_login"] = row["last_login"].isNull() ? "—" : row["last_login"].as<std::string>();
        a["comments"] = row["comments"].isNull() ? "—" : row["comments"].as<std::string>();
        a["created_at"] = row["created_at"].isNull() ? "—" : row["created_at"].as<std::string>();
        a["role"] = row["role"].as<std::string>();
        agents.push_back(a);
    }
    data.insert("agents", agents);
    data.insert("userName", req->getSession()->get<std::string>("userName"));
    data.insert("role", std::string("admin"));
    co_return HttpResponse::newHttpViewResponse("agents.csp", data);
}
drogon::Task<HttpResponsePtr> WebController::trackersList(HttpRequestPtr req) {
    if(!checkSession(req)) co_return HttpResponse::newRedirectionResponse("/login");
    std::string role = req->getSession()->getOptional<std::string>("role").value_or("agent");
    std::string agentSectorId = req->getSession()->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 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::arrayValue);
        pt.append(row["latitude"].as<double>());
        pt.append(row["longitude"].as<double>());
        pointsJson[tid].append(pt);
    }
    Json::FastWriter writer;
    std::string pointsJsonStr = writer.write(pointsJson);
    HttpViewData data;
    std::vector<std::map<std::string, std::string>> trackers;
    for(auto const& row : res) {
        std::string trkSectorId = row["sector_id"].isNull() ? "" : row["sector_id"].as<std::string>();
        if(role == "agent" && trkSectorId != agentSectorId) {
            continue;
        }
        std::map<std::string, std::string> t;
        t["tracker_id"] = row["tracker_id"].as<std::string>();
        t["serial_number"] = row["serial_number"].as<std::string>();
        t["sector_id"] = trkSectorId;
        t["status"] = row["status"].as<std::string>();
        t["rental_start"] = row["rental_start"].isNull() ? "—" : row["rental_start"].as<std::string>();
        t["rental_end"] = row["rental_end"].isNull() ? "—" : row["rental_end"].as<std::string>();
        trackers.push_back(t);
    }
    data.insert("trackers", trackers);
    data.insert("trackers_points_json", pointsJsonStr);
    data.insert("userName", req->getSession()->get<std::string>("userName"));
    data.insert("role", role);
    co_return HttpResponse::newHttpViewResponse("trackers.csp", data);
}
drogon::Task<HttpResponsePtr> WebController::mapView(HttpRequestPtr req) {
    if(!checkSession(req)) co_return HttpResponse::newRedirectionResponse("/login");
    std::string role = req->getSession()->getOptional<std::string>("role").value_or("agent");
    std::string agentSectorId = req->getSession()->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 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>();
        std::string tsr = res[std::stoi(tid)-1]["serial_number"].as<std::string>();
        std::string trkSectorIdP = res[std::stoi(tid)-1]["sector_id"].isNull() ? "" : res[std::stoi(tid)-1]["sector_id"].as<std::string>();
        if(role == "agent" && trkSectorIdP != agentSectorId) {
            continue;
        }
        if(!pointsJson.isMember(tsr)) {

            pointsJson[tsr] = Json::arrayValue;
            
        }
        Json::Value pt(Json::arrayValue);
        pt.append(row["latitude"].as<double>());
        pt.append(row["longitude"].as<double>());
        pointsJson[tsr].append(pt);
        
    }
   
    std::vector<std::map<std::string, std::string>> accessibleTrackers;
    for(auto const& row : res) {
        std::string trkSectorId = row["sector_id"].isNull() ? "" : row["sector_id"].as<std::string>();
        if(role == "agent" && trkSectorId != agentSectorId) {
            continue;
        }
        std::map<std::string, std::string> t;
        t["tracker_id"] = row["tracker_id"].as<std::string>();
        t["serial_number"] = row["serial_number"].as<std::string>();
        t["status"] = row["status"].as<std::string>();
        accessibleTrackers.push_back(t);
    }
    Json::FastWriter writer;
    std::string pointsJsonStr = writer.write(pointsJson);
    HttpViewData data;
    data.insert("trackers", accessibleTrackers);
    data.insert("trackers_points_json", pointsJsonStr);
    data.insert("userName", req->getSession()->get<std::string>("userName"));
    data.insert("role", role);
    co_return HttpResponse::newHttpViewResponse("map.csp", data);
}
drogon::Task<HttpResponsePtr> WebController::addAgent(HttpRequestPtr req) {
    if(!checkSession(req) || !isAdmin(req)) {
        auto resp = HttpResponse::newHttpResponse();
        resp->setStatusCode(k403Forbidden);
        co_return resp;
    }
    auto login = req->getParameter("login");
    auto password = req->getParameter("password");
    auto firstName = req->getParameter("firstName");
    auto lastName = req->getParameter("lastName");
    auto sectorId = req->getParameter("sectorId");
    auto role = req->getParameter("role");
    if(role.empty()) role = "agent";
    try {
        co_await dbService_.insert_sql("Agents", {"0", login, password, firstName, lastName, sectorId, "0", "0", "0", role});
    } catch(const std::exception& e) {
        LOG_ERROR << "addAgent error: " << e.what();
    }
    co_return HttpResponse::newRedirectionResponse("/agents");
}
drogon::Task<HttpResponsePtr> WebController::addTracker(HttpRequestPtr req) {
    if(!checkSession(req) || !isAdmin(req)) {
        auto resp = HttpResponse::newHttpResponse();
        resp->setStatusCode(k403Forbidden);
        co_return resp;
    }
    auto serialNumber = req->getParameter("serialNumber");
    auto sectorId = req->getParameter("sectorId");
    auto status = req->getParameter("status");
    if(status.empty()) status = "active";
    try {
        co_await dbService_.insert_sql("Trackers", {"0", serialNumber, sectorId, status, "0", "0"});
    } catch(const std::exception& e) {
        LOG_ERROR << "addTracker error: " << e.what();
    }
    co_return HttpResponse::newRedirectionResponse("/trackers");
}
drogon::Task<HttpResponsePtr> WebController::updateAgent(HttpRequestPtr req) {
    if(!checkSession(req) || !isAdmin(req)) {
        auto resp = HttpResponse::newHttpResponse();
        resp->setStatusCode(k403Forbidden);
        co_return resp;
    }
    auto agentId = req->getParameter("agentId");
    auto sectorId = req->getParameter("sectorId");
    auto firstName = req->getParameter("firstName");
    auto lastName = req->getParameter("lastName");
    try {
        co_await dbService_.update_sql("Agents",
            {"0", "0", "0", firstName, lastName, sectorId, "0", "0", "0", "0"},
            {agentId, "0", "0", "0", "0", "0", "0", "0", "0", "0"});
    } catch(const std::exception& e) {
        LOG_ERROR << "updateAgent error: " << e.what();
    }
    co_return HttpResponse::newRedirectionResponse("/agents");
}
drogon::Task<HttpResponsePtr> WebController::updateTracker(HttpRequestPtr req) {
    if(!checkSession(req) || !isAdmin(req)) {
        auto resp = HttpResponse::newHttpResponse();
        resp->setStatusCode(k403Forbidden);
        co_return resp;
    }
    auto trackerId = req->getParameter("trackerId");
    auto sectorId = req->getParameter("sectorId");
    auto status = req->getParameter("status");
    auto rentalStart = req->getParameter("rentalStart");
    auto rentalEnd = req->getParameter("rentalEnd");
    try {
        co_await dbService_.update_sql("Trackers",
            {"0", "0", sectorId, status, rentalStart, rentalEnd},
            {trackerId, "0", "0", "0", "0", "0"});
    } catch(const std::exception& e) {
        LOG_ERROR << "updateTracker error: " << e.what();
    }
    co_return HttpResponse::newRedirectionResponse("/trackers");
}