/*-
 * ~~~~~~licensing~~~~~~
 * keystore-runtime
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.security.keystore.runtime.impl;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.entaxy.esb.resources.EntaxyResource;
import ru.entaxy.esb.resources.EntaxyResourceService;
import ru.entaxy.platform.base.support.CommonUtils;
import ru.entaxy.platform.objects.runtime.EntaxyRuntimeObject;
import ru.entaxy.platform.objects.runtime.EntaxyRuntimeObjectService;
import ru.entaxy.security.keystore.runtime.EntaxyKeystore;
import ru.entaxy.security.keystore.runtime.EntaxyKeystoreCausedException;
import ru.entaxy.security.keystore.runtime.EntaxyKeystoreException;
import ru.entaxy.security.keystore.runtime.EntaxyKeystoreNotFoundException;
import ru.entaxy.security.keystore.runtime.EntaxyKeystoreUrlNotDefinedException;
import ru.entaxy.security.keystore.runtime.KeystoreManager;

@Component(service = KeystoreManager.class, immediate = true)
public class KeystoreManagerImpl implements KeystoreManager {

    private static final Logger LOG = LoggerFactory.getLogger(KeystoreManagerImpl.class);

    protected static Map<String, Class<? extends KeystoreHelper>> helpers;

    static {
        helpers = new HashMap<>();
        helpers.put(EntaxyKeystore.KEYSTORE_TYPE_JWK, JWKKeystoreHelper.class);
    }

    @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC,
            policyOption = ReferencePolicyOption.GREEDY)
    volatile EntaxyResourceService entaxyResourceService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC,
            policyOption = ReferencePolicyOption.GREEDY)
    volatile EntaxyRuntimeObjectService entaxyRuntimeObjectService;

    protected class KeystoreDescriptor {
        String url;
        EntaxyRuntimeObject entaxyRuntimeObject;
        EntaxyResource entaxyResource;
    }

    protected KeystoreDescriptor getDescriptor(String keystoreId) throws EntaxyKeystoreException {
        KeystoreDescriptor result = new KeystoreDescriptor();
        result.entaxyRuntimeObject =
                entaxyRuntimeObjectService
                        .getRuntimeObject(keystoreId.concat(":").concat(EntaxyKeystore.KEYSTORE_RUNTIME_TYPE));
        if (result.entaxyRuntimeObject == null)
            throw new EntaxyKeystoreNotFoundException(keystoreId);
        result.url = result.entaxyRuntimeObject.getAttributeOrDefault("keystoreUrl", "").toString();
        if (!CommonUtils.isValid(result.url))
            throw new EntaxyKeystoreUrlNotDefinedException(keystoreId);
        result.entaxyResource = entaxyResourceService.getResource(result.url);
        return result;
    }

    @Override
    public void updateKeystore(String keystoreId, InputStream inputStream) throws EntaxyKeystoreException {
        KeystoreDescriptor descriptor = getDescriptor(keystoreId);
        descriptor.entaxyResource.save(inputStream);
    }

    @Override
    public List<String> getKeystoreNames() {
        return entaxyRuntimeObjectService.getObjects().stream()
                .filter(obj -> obj.getType().equals(EntaxyKeystore.KEYSTORE_RUNTIME_TYPE))
                .map(obj -> obj.getId())
                .collect(Collectors.toList());
    }

    @Override
    public List<String> getKeystoreAliases(String keystoreId) throws EntaxyKeystoreException {
        KeystoreDescriptor descriptor = getDescriptor(keystoreId);
        KeystoreResource resource = new KeystoreResource(descriptor.entaxyResource);
        return resource.getAliases();
    }

    protected KeystoreHelper createHelper(KeystoreResource resource)
            throws EntaxyKeystoreException, UnsupportedOperationException {
        Class<? extends KeystoreHelper> helperClass = helpers.get(resource.getType());

        if (helperClass == null)
            throw new UnsupportedOperationException(
                    String.format("Operation not supported for keystore type [%s]", resource.getType()));

        KeystoreHelper helper = null;
        try {
            helper = helperClass.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException e) {
            throw new EntaxyKeystoreCausedException(resource.keystoreId, e);
        }

        if (helper == null)
            throw new UnsupportedOperationException(
                    String.format("Helper not created for keystore type [%s]", resource.getType()));

        helper.init(resource);
        return helper;
    }

    @Override
    public void addKey(String keystoreId, String alias, String keyContent)
            throws EntaxyKeystoreException, UnsupportedOperationException {
        KeystoreDescriptor descriptor = getDescriptor(keystoreId);
        KeystoreResource resource = new KeystoreResource(descriptor.entaxyResource);

        KeystoreHelper helper = createHelper(resource);
        helper.addKey(alias, keyContent);
    }

    @Override
    public void removeKey(String keystoreId, String alias)
            throws EntaxyKeystoreException, UnsupportedOperationException {
        KeystoreDescriptor descriptor = getDescriptor(keystoreId);
        KeystoreResource resource = new KeystoreResource(descriptor.entaxyResource);

        KeystoreHelper helper = createHelper(resource);
        helper.removeKey(alias);

    }
}
