/*******************************************************************************
 * 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.
 *******************************************************************************/

#pragma once

#include <string>
#include <vector>
#include <map>
#include <cstdint>
#include <iostream>

#ifdef Windows
#define PLUGIN_VISIBILITY __declspec(dllexport)
#else
#define PLUGIN_VISIBILITY __attribute__((visibility("default")))
#endif

namespace authdb {

    class AuthBackend;

    /* Well-known Kerberos encryption type constants (subset for API use) */
    constexpr int KDC_ETYPE_AES128_CTS_HMAC_SHA1 = 17;
    constexpr int KDC_ETYPE_AES256_CTS_HMAC_SHA1 = 18;

    /***************************************************************************
     * Abstract Plugin Base
     *
     * Every authdb plugin inherits from this.  The shared library exports
     * two C functions:
     *
     *   extern "C" authdb::Plugin *authdb_plugin_create(
     *       authdb::AuthBackend &backend,
     *       const char *configJson);
     *
     *   extern "C" void authdb_plugin_destroy(authdb::Plugin *p);
     **************************************************************************/
    class PLUGIN_VISIBILITY Plugin {
    public:
        virtual ~Plugin() = default;

        /* Human-readable name, e.g. "krb5", "ldap" */
        virtual const char *name() const = 0;

        /* Bind sockets and acquire resources that require root.
         * Called before privilege drop.  Default: no-op.        */
        virtual void init() {}

        /* Start the plugin (blocking – caller runs this in a thread) */
        virtual void run() = 0;
    };

    /* C factory signatures every plugin .so must export */
    using PluginCreateFn  = Plugin *(*)(AuthBackend &backend,
                                         const char *configJson);
    using PluginDestroyFn = void (*)(Plugin *p);

    /***************************************************************************
     * KDC Plugin Interface  (extends Plugin)
     **************************************************************************/
    class PLUGIN_VISIBILITY KdcPlugin : public Plugin {
    public:
        /* Derive a Kerberos long-term key from password + salt.
         * etype: 17 = aes128-cts-hmac-sha1-96
         *        18 = aes256-cts-hmac-sha1-96   */
        virtual std::vector<uint8_t> string2key(const std::string &password,
                                                 const std::string &salt,
                                                 int etype) = 0;
    };

    /***************************************************************************
     * Active Directory Plugin Interface  (extends Plugin)
     *
     * Provides LDAP directory services.  Depends on KdcPlugin for
     * SASL/GSSAPI authentication.
     **************************************************************************/
    class PLUGIN_VISIBILITY AdPlugin : public Plugin {
    public:
        /* Get the LDAP base DN for a domain name */
        virtual std::string baseDN(const std::string &domain) const = 0;
    };

    /***************************************************************************
     * PluginManager – loads all .so files from a directory
     *
     * Each loaded plugin is stored by name().  Retrieve a specific subclass
     * with getPlugin<T>("name").
     **************************************************************************/
    class PLUGIN_VISIBILITY PluginManager {
    public:
        PluginManager() = default;
        ~PluginManager();

        /* Scan directory for *.so files, dlopen each, call authdb_plugin_create.
         * configJson is forwarded to every plugin factory.                      */
        int loadAll(const std::string &directory,
                    AuthBackend &backend,
                    const char *configJson = "{}");

        /* Call init() on every loaded plugin (while still root) */
        void initAll();

        /* Retrieve a plugin by name, dynamically cast to T* */
        template<typename T>
        T *getPlugin(const std::string &name) {
            auto it = _Plugins.find(name);
            if (it == _Plugins.end()) return nullptr;
            return dynamic_cast<T*>(it->second.instance);
        }

        /* Iterate over all loaded plugins */
        const auto &plugins() const { return _Plugins; }

    private:
        struct LoadedPlugin {
            void           *handle   = nullptr;
            Plugin         *instance = nullptr;
            PluginDestroyFn destroy  = nullptr;
        };
        std::map<std::string, LoadedPlugin> _Plugins;
    };

    /* Global pointers set by main after loading plugins.
     * Other translation units (admin.cpp, api.cpp) use these
     * without linking to the plugin at build time.            */
    extern KdcPlugin      *g_KdcPlugin;
    extern AdPlugin       *g_AdPlugin;
    extern PluginManager  *g_PluginManager;

};
