package ru.zetit.bridgeandjoins.crypto;

import java.util.logging.Logger;

/**
 * Провайдер на основе АПК «Валидата Клиент L». Через Pki1.LocalIface
 * вызывает нативную JNI-библиотеку libzpki1jni.so. Профиль
 * (имя из pki1.conf или ПК «Справочник сертификатов») берётся из
 * переменной окружения BJ_VALIDATA_PROFILE — если пусто, инициализируем
 * минимальный контекст без доступа к ПСП/ЛСП/ССС (доступны только
 * хэширование, Base64, RAND).
 *
 * Поддерживает горячее переключение профиля через {@link #reinit(String)}
 * — без рестарта сайдкара.
 *
 * Подробности нативного API — в документе ВАМБ.00135-06 33 03
 * «Руководство программиста» (Java) и 33 01 (C).
 */
public final class ValidataProvider implements KeystoreProvider {

    private static final Logger LOG = Logger.getLogger(ValidataProvider.class.getName());

    private Pki1.LocalIface pki;
    private boolean ready;
    private String message;
    private boolean minimal;
    private String profile;

    public ValidataProvider() {
        this.profile = System.getenv("BJ_VALIDATA_PROFILE");
        initLocked();
    }

    /**
     * reinit — атомарная переинициализация провайдера на новый профиль
     * (или пустой = minimal). Закрывает текущий контекст и поднимает
     * новый. Возвращает true при успехе.
     */
    public synchronized boolean reinit(String newProfile) {
        // Уничтожаем старый контекст, если был.
        if (pki != null && ready) {
            try {
                pki.VCERT_Uninitialize();
            } catch (Throwable t) {
                LOG.warning("reinit: VCERT_Uninitialize: " + t.getMessage());
            }
        }
        pki = null;
        ready = false;
        message = "";
        minimal = false;
        this.profile = newProfile;
        initLocked();
        return ready;
    }

    private void initLocked() {
        try {
            this.pki = new Pki1.LocalIface();
            int rc;
            if (profile == null || profile.isEmpty()) {
                rc = pki.VCERT_InitMinimal();
                this.minimal = true;
            } else {
                rc = pki.VCERT_Initialize(profile, 0);
                this.minimal = false;
            }
            if (rc == 0) {
                this.ready = true;
                this.message = minimal
                        ? "Валидата: минимальный контекст (без профиля). Доступны хэш, Base64, RAND."
                        : "Валидата: контекст с профилем «" + profile + "» инициализирован.";
                LOG.info(message);
            } else {
                this.ready = false;
                String err = safeErrorText(rc);
                this.message = (minimal ? "VCERT_InitMinimal" : "VCERT_Initialize(\"" + profile + "\")")
                        + " вернул код " + rc + " (" + err + ")";
                LOG.warning(message);
            }
        } catch (UnsatisfiedLinkError e) {
            this.ready = false;
            this.message = "Валидата: не удалось загрузить libzpki1jni.so — " + e.getMessage()
                    + ". Проверьте java.library.path и установку АПК «Валидата Клиент L».";
            LOG.severe(message);
        } catch (Throwable t) {
            this.ready = false;
            this.message = "Валидата: инициализация упала — " + t.getClass().getSimpleName()
                    + ": " + t.getMessage();
            LOG.severe(message);
        }
    }

    @Override public String name() { return "validata"; }
    @Override public synchronized boolean ready() { return ready; }
    @Override public synchronized String message() { return message; }
    @Override public synchronized Pki1.LocalIface pki() { return ready ? pki : null; }

    /** Признак режима VCERT_InitMinimal (без профиля). */
    public synchronized boolean isMinimal() { return minimal; }

    /** Имя профиля, под которым инициализировались (null/пусто = minimal). */
    public synchronized String profile() { return profile; }

    @Override
    public synchronized void close() {
        if (pki != null && ready) {
            try {
                pki.VCERT_Uninitialize();
            } catch (Throwable t) {
                LOG.warning("Валидата: VCERT_Uninitialize: " + t.getMessage());
            }
        }
    }

    private String safeErrorText(int rc) {
        try {
            String t = pki.VCERT_GetErrorText(rc);
            return t == null ? "" : t;
        } catch (Throwable ignore) {
            return "";
        }
    }
}
