/*******************************************************************************
 * 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 <iostream>
#include <algorithm>
#include <iterator>
#include <memory>
#include <uuidp.h>

#include "types.h"
#include "user.h"
#include "index.h"
#include "exception.h"

authdb::UserData::UserData(const uuid::uuid &id) : AuthData(id){
    username=nullptr;
    firstname=nullptr;
    lastname=nullptr;
    mail=nullptr;
    pwhash=nullptr;
    krbkey=nullptr;
    avatar=nullptr;
    Data.type=DataType::UserData;
}

authdb::UserData::~UserData(){
}

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

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


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

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


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

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


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

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

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

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


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

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

void authdb::UserData::setAvatar(const std::vector<char>& src){
    setAuthData<std::vector<char>>(DataType::UserData,StorageType::BinaryStorage,this,&avatar,"avatar",&src,src.size());
}

void authdb::UserData::getAvatar(std::vector<char>& dest){
    if(!avatar)
        getAuthData<std::vector<char>>(DataType::UserData,StorageType::BinaryStorage,this,&avatar,"avatar");

    if(avatar && avatar->data)
        std::copy(avatar->data,avatar->data+avatar->datasize,
                  std::back_inserter(dest));
    else
        throw(authdb::AuthBackendError("Avatar data nout found !"));
}

void authdb::User::create(AuthBackend &backend,class UserData* dat){

    int exi=0;

    if(exits(backend,dat->Data.ruid,dat->getUsername(),exi)){
       switch(exi){
           case 1:
                throw authdb::AuthBackendError("Create User: Username already Exists aborting!");
           default:
                throw authdb::AuthBackendError("Create User: UUID already Exists aborting!");
       }
    }

    createRecord(backend,*dat,DataType::UserData);

}

bool authdb::User::exits(AuthBackend& backend,const uuid::uuid &uid, const std::string &name,int &ret){
    RecordIndex &idx = getIndex(backend.getOptions());
    if (!idx.valid()) {
        idx.build(backend);
    }

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

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

    ret = -1;
    return false;
}


void authdb::User::edit(AuthBackend &backend,class UserData& dat, size_t &pos){
    remove(backend,dat.Data.ruid);
    create(backend,&dat);
}

void authdb::User::remove(AuthBackend &backend,const uuid::uuid &uid){
    delRecord(backend,uid,DataType::UserData);
}

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

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

    while(pos+sizeof(AuthData::Record)<=end){
        std::shared_ptr<authdb::AuthData::Record> cur=std::make_shared<authdb::AuthData::Record>();
        backend.setPos(pos);
        backend.read(reinterpret_cast<unsigned char*>(cur.get()),sizeof(AuthData::Record));
        size_t next=backend.getPos()+cur->datasize;
        if(next<backend.getPos() || next>end) break;
        pos=next;
        if(cur->type == UserData && uuid::uuid(dat.Data.ruid)==cur->ruid
            && strcmp(cur->fieldname,"username")==0
        ){

            if(!getRecord(backend,dat,DataType::UserData))
                continue;

            if(strcmp(dat.Data.fieldname,"username")==0){
                dat.username=&dat.Data;
            }
        }
    }
}




