/*******************************************************************************
 * Copyright (c) 2026, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include <iostream>
#include <cstring>

#include "service.h"
#include "exception.h"
#include "backend.h"

authdb::ServiceData::ServiceData(uuid::uuid sid) : AuthData(sid.value) {
    _ServiceName = nullptr;
    _HostName    = nullptr;
    _ClientId    = nullptr;
    _KrbKey      = nullptr;
    _Secret      = nullptr;
}

authdb::ServiceData::~ServiceData() {
}

void authdb::ServiceData::setServiceName(const char *name) {
    setAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                      this, &_ServiceName, "servicename", name, strlen(name) + 1);
}

const char *authdb::ServiceData::getServiceName() {
    return getAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                             this, &_ServiceName, "servicename");
}

void authdb::ServiceData::setHostName(const char *hostname) {
    setAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                      this, &_HostName, "hostname", hostname, strlen(hostname) + 1);
}

const char *authdb::ServiceData::getHostName() {
    return getAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                             this, &_HostName, "hostname");
}

void authdb::ServiceData::setClientId(const char *clientid) {
    setAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                      this, &_ClientId, "clientid", clientid, strlen(clientid) + 1);
}

const char *authdb::ServiceData::getClientId() {
    return getAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                             this, &_ClientId, "clientid");
}

void authdb::ServiceData::setSecret(const char *secret) {
    setAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                      this, &_Secret, "servicesecret", secret, strlen(secret) + 1);
}

const char *authdb::ServiceData::getSecret() {
    return getAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                             this, &_Secret, "servicesecret");
}

void authdb::ServiceData::setKrbKey(const std::string &src) {
    setAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                      this, &_KrbKey, "krbkey", src.c_str(), src.length() + 1);
}

const std::string authdb::ServiceData::getKrbKey() {
    try {
        const char *res = getAuthData<char>(DataType::ServiceData, StorageType::TextStorage,
                                            this, &_KrbKey, "krbkey");
        if (res)
            return res;
        else
            return "";
    } catch (AuthBackendError &e) {
        std::cerr << e.what() << std::endl;
        return "";
    }
}

void authdb::ServiceManager::create(AuthBackend &backend, class ServiceData *data) {
    int exi = 0;

    if (exits(backend, data->Data.ruid, data->getServiceName(), exi)) {
        switch (exi) {
            case 1:
                throw authdb::AuthBackendError("Create Service: Servicename already Exists aborting!");
            default:
                throw authdb::AuthBackendError("Create Service: UUID already Exists aborting!");
        }
    }

    createRecord(backend, *data, DataType::ServiceData);
}

bool authdb::ServiceManager::exits(AuthBackend &backend, const uuid::uuid &sid,
                                    const char *name, int &ret) {
    authdb::AuthData::Record cur;

    size_t rd = sizeof(authdb::AuthHeader), end = backend.end();

    while (rd < end) {
        backend.setPos(rd);
        backend.read((unsigned char *)&cur, sizeof(AuthData::Record));
        rd = backend.getPos() + cur.datasize;
        if (strcmp(cur.fieldname, "servicename") == 0) {
            cur.data = new char[cur.datasize];
            backend.read((unsigned char *)cur.data, cur.datasize);
            if (name && strcmp(cur.data, name) == 0) {
                delete[] cur.data;
                ret = 1;
                return true;
            } else if (!sid.empty() && sid == cur.ruid) {
                delete[] cur.data;
                ret = 2;
                return true;
            }
            delete[] cur.data;
        }
    }
    ret = -1;
    return false;
}

void authdb::ServiceManager::info(AuthBackend &backend, class ServiceData &dat, size_t &pos) {
    size_t end = backend.end();

    if (uuid::uuid(dat.Data.ruid).empty())
        throw AuthBackendError("service info uid required!");

    while (pos < end) {
        authdb::AuthData::Record *cur = new AuthData::Record;
        backend.setPos(pos);
        backend.read((unsigned char *)cur, sizeof(AuthData::Record));
        pos = backend.getPos() + cur->datasize;
        if (cur->type == ServiceData && uuid::uuid(dat.Data.ruid) == cur->ruid
            && strcmp(cur->fieldname, "servicename") == 0)
        {
            if (!getRecord(backend, dat, DataType::ServiceData))
                continue;

            dat._ServiceName = &dat.Data;
        }
        delete cur;
    }
}

void authdb::ServiceManager::remove(AuthBackend &backend, const uuid::uuid &sid) {
    delRecord(backend, sid.value, DataType::ServiceData);
}
