/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <streambuf>
#include "scan_log.h"
#include "scan_usb_manager.h"
#include "scan_util.h"
#include "scan_system_data.h"
#include "scan_service_ability.h"

namespace {
const std::string SCANNER_LIST_FILE = "/data/service/el2/public/print_service/sane/tmp/scanner_list.json";
const std::string SCANNER_LIST_VERSION = "v1";
}  // namespace

namespace OHOS {
namespace Scan {
std::map<std::string, std::string> ScanSystemData::usbSnToPortMap_;
bool ScanSystemData::CheckJsonObjectValue(const nlohmann::json& object)
{
    const std::vector<std::string> keyList = {"deviceId", "manufacturer", "model", "deviceType",
        "discoverMode", "serialNumber", "deviceName", "uniqueId"};
    for (auto key : keyList) {
        if (!object.contains(key) || !object[key].is_string()) {
            SCAN_HILOGW("can not find %{public}s", key.c_str());
            return false;
        }
    }
    return true;
}

bool ScanSystemData::ParseScannerListJsonV1(nlohmann::json& jsonObject)
{
    if (!jsonObject.contains("scaner_list") || !jsonObject["scaner_list"].is_array()) {
        SCAN_HILOGW("can not find scaner_list");
        return false;
    }
    for (auto &element : jsonObject["scaner_list"].items()) {
        nlohmann::json object = element.value();
        if (!CheckJsonObjectValue(object)) {
            continue;
        }
        ScanDeviceInfo scanDeviceInfo;
        scanDeviceInfo.deviceId = object["deviceId"];
        scanDeviceInfo.manufacturer = object["manufacturer"];
        scanDeviceInfo.model = object["model"];
        scanDeviceInfo.deviceType = object["deviceType"];
        scanDeviceInfo.discoverMode = object["discoverMode"];
        scanDeviceInfo.serialNumber = object["serialNumber"];
        scanDeviceInfo.deviceName = object["deviceName"];
        scanDeviceInfo.uniqueId = object["uniqueId"];
        scanDeviceInfo.uuid = object["uuid"];
        std::string uniqueId = scanDeviceInfo.discoverMode + scanDeviceInfo.uniqueId;
        InsertScannerInfo(uniqueId, scanDeviceInfo);
    }
    RefreshUsbDeviceId();
    return true;
}

bool ScanSystemData::Init()
{
    addedScannerMap_.clear();
    std::ifstream ifs(SCANNER_LIST_FILE.c_str(), std::ios::in | std::ios::binary);
    if (!ifs.is_open()) {
        SCAN_HILOGW("open scanner list file fail");
        return false;
    }
    std::string fileData((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
    ifs.close();
    if (!nlohmann::json::accept(fileData)) {
        SCAN_HILOGW("json accept fail");
        return false;
    }
    nlohmann::json jsonObject = nlohmann::json::parse(fileData);
    if (!jsonObject.contains("version") || !jsonObject["version"].is_string()) {
        SCAN_HILOGW("can not find version");
        return false;
    }
    std::string version = jsonObject["version"].get<std::string>();
    SCAN_HILOGI("json version: %{public}s", version.c_str());
    if (version == SCANNER_LIST_VERSION) {
        return ParseScannerListJsonV1(jsonObject);
    }
    return false;
}

void ScanSystemData::RefreshUsbDeviceId()
{
    ScanUsbManager::GetInstance()->RefreshUsbDevicePort();
    if (usbSnToPortMap_.empty()) {
        SCAN_HILOGW("Failed to refresh the USB device.");
        return;
    }
    for (auto &scanDevIt : addedScannerMap_) {
        std::string discoverMode = scanDevIt.second->discoverMode;
        if (discoverMode == "TCP") {
            continue;
        }
        std::string serialNumber = scanDevIt.second->serialNumber;
        auto it = usbSnToPortMap_.find(serialNumber);
        if (it == usbSnToPortMap_.end()) {
            continue;
        }
        std::string oldDeviceId = scanDevIt.second->deviceId;
        std::string usbPort = it->second;
        std::string newDeviceId = ReplaceDeviceIdUsbPort(oldDeviceId, usbPort);
        if (newDeviceId == "" || newDeviceId == oldDeviceId) {
            SCAN_HILOGD("cannot update usb deviceId.");
            continue;
        }
        scanDevIt.second->deviceId = newDeviceId;
        ScanDeviceInfoSync syncInfo;
        syncInfo.deviceId = newDeviceId;
        syncInfo.serialNumber = serialNumber;
        syncInfo.oldDeviceId = oldDeviceId;
        syncInfo.discoverMode = "USB";
        auto saPtr = ScanServiceAbility::GetInstance();
        if (saPtr == nullptr) {
            SCAN_HILOGE("saPtr is a nullptr");
            return;
        }
        saPtr->UpdateScannerId(syncInfo);
    }
    if (!SaveScannerMap()) {
        SCAN_HILOGW("Failed to save the JSON file.");
    }
}

std::string ScanSystemData::ReplaceDeviceIdUsbPort(const std::string& deviceId, const std::string& usbPort)
{
    constexpr int32_t invalidPort = -1;
    int32_t start = invalidPort;
    int32_t end = invalidPort;
    char dash;
    std::istringstream(usbPort) >> start >> dash >> end;
    if (start < 0 || end < 0 || dash != '-') {
        SCAN_HILOGE("usbPort format is error");
        return "";
    }
    std::ostringstream oss;
    char zero = '0';
    constexpr int32_t portWidth = 3;
    oss << std::setw(portWidth) << std::setfill(zero) << start;
    std::string formattedStart = oss.str();
    oss.str("");
    oss << std::setw(portWidth) << std::setfill(zero) << end;
    std::string formattedEnd = oss.str();
    size_t pos1 = deviceId.rfind(':');
    if (pos1 == std::string::npos) {
        SCAN_HILOGE("deviceId format is error");
        return "";
    }
    size_t pos2 = deviceId.rfind(':', pos1 - 1);
    if (pos2 == std::string::npos) {
        SCAN_HILOGE("deviceId format is error");
        return "";
    }
    std::string newDeviceId = deviceId.substr(0, pos2 + 1).append(formattedStart).append(":").append(formattedEnd);
    SCAN_HILOGD("new deviceId = %{private}s", newDeviceId.c_str());
    return newDeviceId;
}

std::string ScanSystemData::GetNewDeviceId(std::string oldDeviceId, std::string usbDevicePort)
{
    if (oldDeviceId.find_last_of(":") == std::string::npos ||
        oldDeviceId.find_last_of(":") < USB_DEVICEID_FIRSTID_LEN_3 ||
        oldDeviceId.find_last_of("-") == std::string::npos ||
        oldDeviceId.find_last_of("-") < 1) {
        return nullptr;
    }
    std::string deviceIdHead = oldDeviceId.substr(0, oldDeviceId.find_last_of(":") - USB_DEVICEID_FIRSTID_LEN_3);
    std::string firstPort = usbDevicePort.substr(0, usbDevicePort.find("-"));
    std::string secondPort = usbDevicePort.substr(usbDevicePort.find("-") + 1, usbDevicePort.size() - 1);
    SCAN_HILOGI("firstPort = %{public}s, secondPort = %{public}s.",
                firstPort.c_str(), secondPort.c_str());
    FormatUsbPort(firstPort);
    FormatUsbPort(secondPort);
    return deviceIdHead + firstPort + ":" + secondPort;
}

void ScanSystemData::FormatUsbPort(std::string &port)
{
    port.insert(0, USB_DEVICEID_FIRSTID_LEN_3 - port.size(), '0');
}

bool ScanSystemData::UpdateScannerIdByUsbDevicePort(const std::string &uniqueId, const std::string &usbDevicePort)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    auto iter = addedScannerMap_.find(uniqueId);
    if (iter != addedScannerMap_.end() && iter->second != nullptr) {
        std::string oldDeviceId = iter->second->deviceId;
        std::string newDeviceId = ReplaceDeviceIdUsbPort(oldDeviceId, usbDevicePort);
        SCAN_HILOGD("newDeviceId : %{private}s", newDeviceId.c_str());
        if (newDeviceId == "" || newDeviceId == oldDeviceId) {
            SCAN_HILOGD("cannot update usb deviceId.");
            return false;
        }
        iter->second->deviceId = newDeviceId;
    } else {
        SCAN_HILOGE("ScanSystemData UpdateScannerIdByUsbDevicePort fail");
        return false;
    }
    return true;
}

void ScanSystemData::InsertScannerInfo(const std::string &uniqueId, const ScanDeviceInfo &scannerInfo)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    auto iter = addedScannerMap_.find(uniqueId);
    if (iter == addedScannerMap_.end() || iter->second == nullptr) {
        SCAN_HILOGI("insert new scanner");
        addedScannerMap_[uniqueId] = std::make_shared<ScanDeviceInfo>(scannerInfo);
    } else {
        SCAN_HILOGI("update exist scanner");
        iter->second->deviceId = scannerInfo.deviceId;
        iter->second->manufacturer = scannerInfo.manufacturer;
        iter->second->model = scannerInfo.model;
        iter->second->deviceType = scannerInfo.deviceType;
        iter->second->serialNumber = scannerInfo.serialNumber;
        iter->second->deviceName = scannerInfo.deviceName;
        iter->second->uniqueId = scannerInfo.uniqueId;
        iter->second->uuid = scannerInfo.uuid;
    }
}

bool ScanSystemData::DeleteScannerInfo(const std::string &uniqueId)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    auto iter = addedScannerMap_.find(uniqueId);
    if (iter != addedScannerMap_.end()) {
        addedScannerMap_.erase(uniqueId);
    } else {
        SCAN_HILOGE("ScanSystemData delete connected scanner fail");
        return false;
    }
    return true;
}

bool ScanSystemData::UpdateScannerNameByUniqueId(const std::string &uniqueId, const std::string &deviceName)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    auto iter = addedScannerMap_.find(uniqueId);
    if (iter != addedScannerMap_.end() && iter->second != nullptr) {
        iter->second->deviceName = deviceName;
    } else {
        SCAN_HILOGE("ScanSystemData UpdateScannerNameByUniqueId fail");
        return false;
    }
    return true;
}

bool ScanSystemData::UpdateScannerInfoByUniqueId(const std::string &uniqueId, const ScanDeviceInfo &scannerInfo)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    auto iter = addedScannerMap_.find(uniqueId);
    if (iter != addedScannerMap_.end()) {
        iter->second->deviceId = scannerInfo.deviceId;
        iter->second->model = scannerInfo.model;
        iter->second->deviceType = scannerInfo.deviceType;
        iter->second->serialNumber = scannerInfo.serialNumber;
        iter->second->deviceName = scannerInfo.deviceName;
        iter->second->uniqueId = scannerInfo.uniqueId;
        return true;
    }
    SCAN_HILOGE("ScanSystemData UpdateScannerInfoByUniqueId not found scannerInfo");
    return false;
}

bool ScanSystemData::QueryScannerNameByUniqueId(const std::string &uniqueId, std::string &deviceName)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    auto iter = addedScannerMap_.find(uniqueId);
    if (iter != addedScannerMap_.end()) {
        deviceName = iter->second->deviceName;
        return true;
    }
    SCAN_HILOGW("QueryScannerNameByUniqueId fail");
    return false;
}

bool ScanSystemData::QueryScannerInfoByUniqueId(const std::string &uniqueId, ScanDeviceInfo &scannerInfo)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    for (auto iter = addedScannerMap_.begin(); iter != addedScannerMap_.end(); ++iter) {
        auto info = iter->second;
        if (info == nullptr) {
            continue;
        }
        std::string iterSn = info->discoverMode + info->serialNumber;
        std::string iterUniqueId = info->discoverMode + info->uniqueId;
        if (uniqueId == iterSn || uniqueId == iterUniqueId) {
            scannerInfo.deviceId = info->deviceId;
            scannerInfo.manufacturer = info->manufacturer;
            scannerInfo.model = info->model;
            scannerInfo.deviceType = info->deviceType;
            scannerInfo.discoverMode = info->discoverMode;
            scannerInfo.serialNumber = info->serialNumber;
            scannerInfo.deviceName = info->deviceName;
            scannerInfo.uniqueId = info->uniqueId;
            return true;
        }
    }
    return false;
}

void ScanSystemData::GetAddedScannerInfoList(std::vector<ScanDeviceInfo> &infoList)
{
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    for (auto iter = addedScannerMap_.begin(); iter != addedScannerMap_.end(); ++iter) {
        if (iter->second != nullptr) {
            infoList.push_back(*(iter->second));
        }
    }
}

bool ScanSystemData::SaveScannerMap()
{
    int32_t fd = open(SCANNER_LIST_FILE.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0640);
    SCAN_HILOGD("SaveScannerMap fd: %{public}d", fd);
    if (fd < 0) {
        SCAN_HILOGW("Failed to open file errno: %{public}s", std::to_string(errno).c_str());
        return false;
    }
    nlohmann::json scannerMapJson = nlohmann::json::array();
    {
        std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
        for (auto iter = addedScannerMap_.begin(); iter != addedScannerMap_.end(); ++iter) {
            auto info = iter->second;
            if (info == nullptr) {
                continue;
            }
            nlohmann::json scannerJson = nlohmann::json::object();
            scannerJson["deviceId"] = info->deviceId;
            scannerJson["manufacturer"] = info->manufacturer;
            scannerJson["model"] = info->model;
            scannerJson["deviceType"] = info->deviceType;
            scannerJson["discoverMode"] = info->discoverMode;
            scannerJson["serialNumber"] = info->serialNumber;
            scannerJson["deviceName"] = info->deviceName;
            scannerJson["uniqueId"] = info->uniqueId;
            scannerJson["uuid"] = info->uuid;
            scannerMapJson.push_back(scannerJson);
        }
    }
    nlohmann::json jsonObject;
    jsonObject["version"] = SCANNER_LIST_VERSION;
    jsonObject["scaner_list"] = scannerMapJson;
    std::string jsonString = jsonObject.dump();
    size_t jsonLength = jsonString.length();
    auto writeLength = write(fd, jsonString.c_str(), jsonLength);
    close(fd);
    fd = -1;
    SCAN_HILOGI("SaveScannerMap finished");
    if (writeLength < 0) {
        return false;
    }
    return (size_t)writeLength == jsonLength;
}

bool ScanSystemData::IsContainScanner(const std::string &uniqueId)
{
    if (addedScannerMap_.find(uniqueId) != addedScannerMap_.end()) {
        SCAN_HILOGI("The map contains the scanner.");
        return true;
    } else {
        SCAN_HILOGW("The scanner is not included in the map.");
        return false;
    }
}

std::pair<std::string, std::string> ScanSystemData::UpdateNetScannerByUuid(const std::string &uuid,
    const std::string& ip)
{
    std::string oldKey;
    std::shared_ptr<ScanDeviceInfo> scannerInfo;
    std::lock_guard<std::mutex> autoLock(addedScannerMapLock_);
    for (const auto& [key, info] : addedScannerMap_) {
        if (info != nullptr && info->uuid == uuid) {
            oldKey = key;
            scannerInfo = info;
            break;
        }
    }
    if (oldKey == "" || scannerInfo == nullptr) {
        SCAN_HILOGE("Cannot find scanner by uuid");
        return std::make_pair("", "");
    }
    std::string oldDeviceId = scannerInfo->deviceId;
    std::string newDeviceId = ScanUtil::ReplaceIpAddress(oldDeviceId, ip);
    if (newDeviceId == scannerInfo->deviceId) {
        SCAN_HILOGE("Get new device Id fail.");
        return std::make_pair("", "");
    }
    SCAN_HILOGD("newdeviceId = %{private}s", newDeviceId.c_str());
    addedScannerMap_.erase(oldKey);
    std::string newKey = "TCP" + ip;
    scannerInfo->deviceId = newDeviceId;
    scannerInfo->uniqueId = ip;
    auto it = addedScannerMap_.find(newKey);
    if (it == addedScannerMap_.end()) {
        addedScannerMap_.insert(std::make_pair(newKey, scannerInfo));
    } else {
        it->second = scannerInfo;
    }
    return std::make_pair(oldDeviceId, newDeviceId);
}

}  // namespace Scan
}  // namespace OHOS