/*******************************************************************************
 * Copyright (c) 2025, 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 <cstring>
#ifdef Windows
    #include <Windows.h>
#else
    #include <unistd.h>
#endif

#include "domain.h"
#include "authdb.h"
#include "plugin.h"
#include "index.h"
#include "types.h"

authdb::DomainData::DomainData(uuid::uuid duuid) :AuthData(duuid){
    _name=nullptr;
    _storageType=nullptr;
    _storageOptions=nullptr;
}

authdb::DomainData::~DomainData(){
}

void authdb::DomainData::setDomainName(const std::string &name){
    setAuthData<char>(DataType::DomainData,StorageType::TextStorage,this,&_name,"domainname",name.c_str(),name.length()+1);
}
const std::string authdb::DomainData::getDomainName(){
    return getAuthData<char>(DataType::DomainData,StorageType::TextStorage,this,&_name,"domainname");
}


void authdb::DomainData::setStorageType(const int type){
    setAuthData<int>(DataType::DomainData,StorageType::IntStorage,this,&_storageType,"domainstorage",&type,sizeof(int));
}

int authdb::DomainData::getStorageType(){
    return *getAuthData<int>(DataType::DomainData,StorageType::IntStorage,this,&_storageType,"domainstorage");
}

void authdb::DomainData::setStorageOptions(const std::string &opts){
    return setAuthData<char>(DataType::DomainData,StorageType::TextStorage,this,&_storageOptions,"domainoptions",opts.c_str(),opts.length()+1);
}

const std::string authdb::DomainData::getStorageOptions(){
    return getAuthData<char>(DataType::DomainData,StorageType::TextStorage,
                             this,&_storageOptions,"domainoptions");
}

void authdb::Domain::create(authdb::AuthBackend& backend,class DomainData *data){
    AuthBackend::Guard guard(backend);
    int exi=0;


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

    backend.createRecord(*data,DataType::DomainData);
}

void authdb::Domain::remove(AuthBackend& backend, const uuid::uuid &uid){
    backend.delRecord(uid,DataType::DomainData);
}


bool authdb::Domain::exits(AuthBackend& backend,const uuid::uuid& did, const std::string &name,int &ret){
    AuthBackend::Guard guard(backend, AuthBackend::Shared);
    RecordIndex &idx = backend.getIndex();
    if (!idx.valid() || idx.revision() != backend.getRevesion()) {
        idx.build(backend);
    }

    if (!name.empty()) {
        uuid::uuid found;
        if (idx.findByName(DataType::DomainData, "domainname", name, found)) {
            ret = 1;
            return true;
        }
    }

    if (!did.empty() && idx.findByUuid(DataType::DomainData, did)) {
        ret = 2;
        return true;
    }

    ret = -1;
    return false;
}

void authdb::Domain::info(AuthBackend& backend, class DomainData& dat, size_t& pos){
    if(uuid::uuid(dat.Data.ruid).empty())
        throw AuthBackendError("Domain info domainid required!");

    if(!backend.getRecord(dat,DataType::DomainData))
        return;

    if(strcmp(dat.Data.fieldname,"domainname")==0)
        dat._name=&dat.Data;
}

void authdb::Domain::list(AuthBackend &backend,std::vector<uuid::uuid> &uuids){
    AuthBackend::Guard guard(backend, AuthBackend::Shared);
    RecordIndex &idx = backend.getIndex();
    if(!idx.valid() || idx.revision()!=backend.getRevesion())
        idx.build(backend);
    idx.listByType(DataType::DomainData, uuids);
}

namespace authdb {
    KdcPlugin *g_KdcPlugin = nullptr;

    DomainBackend::DomainBackend(authdb::AuthBackend &adminbck) : _adminbck(adminbck) {
        _domainbck=nullptr;
    }

    DomainInfo DomainBackend::resolve(::uuid::uuid did){
        DomainInfo info;
        size_t rd=sizeof(authdb::AuthHeader),end=_adminbck.end();

        while(rd+sizeof(AuthData::Record)<=end){
            authdb::AuthData::Record cur;

            _adminbck.setPos(rd);
            _adminbck.read((unsigned char*)&cur,sizeof(AuthData::Record));

            size_t next=_adminbck.getPos()+cur.datasize;
            if(next<_adminbck.getPos() || next>end) break;
            rd=next;

            if(did == cur.ruid && cur.type == DataType::DomainData &&
                strcmp(cur.fieldname,"domainname")==0){

                class DomainData ddat(cur.ruid);

                size_t dpos=sizeof(authdb::AuthHeader);

                Domain domain;
                domain.info(_adminbck,ddat,dpos);

                info.storageType   = ddat.getStorageType();
                info.storageOptions = ddat.getStorageOptions();
                info.domainName    = ddat.getDomainName();
                info.found         = true;
                return info;
            }
        }
        return info;
    }

    authdb::AuthBackend *DomainBackend::create(const DomainInfo &info){
        if(!info.found)
            return nullptr;
        _domainbck = new AuthBackend(info.storageType,info.storageOptions,info.domainName);
        return _domainbck;
    }

    authdb::AuthBackend *DomainBackend::data(::uuid::uuid did){
        DomainInfo info = resolve(did);
        return create(info);
    }

    DomainBackend::~DomainBackend(){
        delete _domainbck;
    }
}
