/*-
 * ~~~~~~licensing~~~~~~
 * entaxy-audit
 * ==========
 * 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.audit.utils;

import com.google.gson.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.entaxy.audit.data.AuditEvent;
import ru.entaxy.audit.data.Severity;

import javax.security.auth.Subject;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

public class AuditHelper {
    public static final String TARGET_ACTION = "action";

    public static final Severity DEFAULT_SEVERITY = Severity.IMPORTANT;
    public static final String DEFAULT_TARGET = TARGET_ACTION;
    private static final Logger AUDIT = LoggerFactory.getLogger(Constants.LOG_FACILITY_NAME);

    private static final Logger logger = LoggerFactory.getLogger(AuditHelper.class);

    public static void log(AuditEvent event) {
        JsonObject logRecord = new JsonObject();
        logRecord.addProperty("isEmdevEvents", true);
        logRecord.addProperty("typ", event.getTarget());
        logRecord.addProperty("action", event.getCategory());
        logRecord.addProperty("performedBy", event.getSuser());

        JsonObject info = new JsonObject();
        if (event.getSrc() != null) {
            info.addProperty("src", event.getSrc());
        }
        if (event.getDuser() != null) {
            info.addProperty("duser", event.getDuser());
        }
        info.addProperty("suser", event.getSuser());
        info.addProperty("severity", event.getSeverity().getValue());
        info.addProperty("msg", event.getMessage());
        info.addProperty("outcome", event.getOutcome().getLabel());
        logRecord.add("info", info);

        AUDIT.info(logRecord.toString());
    }

    public static String findRemoteUser(Subject subject) {
        return findAndProcessPrincipal(subject, "UserPrincipal", Principal::getName);
    }

    private static String findAndProcessPrincipal(Subject subject, String principalName, Function<Principal, String> mapping) {
        Set<Principal> principals = subject.getPrincipals();
        if (principals != null) {
            Optional<String> userName = principals.stream()
                    .filter(p -> p.getClass().getSimpleName().startsWith(principalName)).findFirst()
                    .map(mapping);
            return userName.orElse(null);
        }
        return null;
    }

    public static String findAddress(Subject subject) {
        return findAndProcessPrincipal(subject, "ClientPrincipal", AuditHelper::getAddress);
    }

    private static String getAddress(Principal p) {
        //We're assumiming to process Karaf's ClientPrincipal. We need method getAddress();
        try {
            Method m = p.getClass().getMethod("getAddress");
            Object result = m.invoke(p);
            return (String) result;
        } catch (Throwable t) {
            logger.error("Cannot get address from principal {}:", p, t);
            return null;
        }
    }
}
