/*******************************************************************************
 * 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>
#include <vector>
#include <string>

#include <netplus/crypto/base64.h>
#include <uuidp.h>

#include "import.h"
#include "types.h"

namespace authdb {

    int Import::fromJson(AuthBackend &backend, json_object *jroot) {
        AuthBackend::Guard guard(backend);
        if (!jroot)
            throw AuthBackendError("import: invalid JSON!");

        json_object *jrecords = json_object_object_get(jroot, "records");
        if (!jrecords || !json_object_is_type(jrecords, json_type_array))
            throw AuthBackendError("import: missing or invalid 'records' array!");

        int count = 0;
        size_t len = json_object_array_length(jrecords);

        for (size_t i = 0; i < len; ++i) {
            json_object *jrec = json_object_array_get_idx(jrecords, i);
            if (!jrec) continue;

            json_object *jruid      = json_object_object_get(jrec, "ruid");
            json_object *jtype      = json_object_object_get(jrec, "type");
            json_object *jstorage   = json_object_object_get(jrec, "storage");
            json_object *jfieldname = json_object_object_get(jrec, "fieldname");
            json_object *jdatasize  = json_object_object_get(jrec, "datasize");
            json_object *jdata      = json_object_object_get(jrec, "data");

            if (!jruid || !jtype || !jstorage || !jfieldname)
                continue;

            // Build the record
            AuthData::Record rec{};
            rec.start = 0xFE;

            // Parse ruid
            uuid::uuid uid;
            uid.parse(json_object_get_string(jruid));
            std::copy(&uid.value[0], &uid.value[16], &rec.ruid[0]);

            rec.type    = json_object_get_int(jtype);
            rec.storage = json_object_get_int(jstorage);

            // Skip empty records
            if (rec.type == EmptyData)
                continue;

            memset(rec.fieldname, 0, sizeof(rec.fieldname));
            strncpy(rec.fieldname, json_object_get_string(jfieldname), sizeof(rec.fieldname) - 1);

            // Base64 decode the data
            std::vector<char> rawdata;
            if (jdata) {
                const char *b64str = json_object_get_string(jdata);
                if (b64str && strlen(b64str) > 0) {
                    netplus::base64 b64;
                    b64.setEncodedData(std::string(b64str));
                    b64 >> rawdata;
                }
            }

            if (!rawdata.empty()) {
                rec.datasize = rawdata.size();
            } else if (jdatasize) {
                rec.datasize = static_cast<size_t>(json_object_get_int64(jdatasize));
            } else {
                rec.datasize = 0;
            }

            // Write record to backend
            size_t wd = backend.end();
            backend.setPos(wd);
            backend.write(reinterpret_cast<unsigned char*>(&rec), sizeof(AuthData::Record));
            if (!rawdata.empty()) {
                backend.write(reinterpret_cast<unsigned char*>(rawdata.data()), rawdata.size());
            }

            ++count;
        }

        // Ensure imported data is durably pushed to the cluster (synchronous)
        // before returning, so a restart won't lose the import.
        backend.flushSync();

        return count;
    }
};

