package com.magistr.app.config.tenant;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.magistr.app.config.tenant.health.TenantReadinessRegistry;
import com.magistr.app.service.TenantLifecycleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import java.util.List;

/**
 * Периодически перечитывает tenants.json (mounted Secret).
 * Если Secret был обновлён через K8s API, этот компонент
 * подхватит изменения и синхронизирует in-memory datasource'ы.
 *
 * Также отвечает за инициализацию БД (init.sql) для новых тенантов.
 */
@Component
public class TenantConfigWatcher {

    private static final Logger log = LoggerFactory.getLogger(TenantConfigWatcher.class);

    private final TenantLifecycleService tenantLifecycleService;
    private final TenantReadinessRegistry readinessRegistry;
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Value("${app.tenants.config-path:tenants.json}")
    private String tenantsConfigPath;

    @Value("${app.tenants.config-required:false}")
    private boolean tenantsConfigRequired;

    // Хеш последнего прочитанного конфига — чтобы не перезагружать зря
    private String lastConfigHash = "";
    private boolean configurationUnavailable;

    public TenantConfigWatcher(TenantLifecycleService tenantLifecycleService,
                               TenantReadinessRegistry readinessRegistry) {
        this.tenantLifecycleService = tenantLifecycleService;
        this.readinessRegistry = readinessRegistry;
    }

    /**
     * Каждые 30 секунд проверяет, изменился ли tenants.json.
     */
    @Scheduled(fixedDelay = 30_000, initialDelay = 30_000)
    public void watchForChanges() {
        tenantLifecycleService.executeSerialized(this::watchForChangesSerialized);
    }

    private void watchForChangesSerialized() {
        try {
            File file = new File(tenantsConfigPath);
            if (!file.exists()) {
                if (tenantsConfigRequired) {
                    readinessRegistry.markConfigurationFailure();
                    if (!configurationUnavailable) {
                        log.error("Обязательный файл конфигурации тенантов недоступен");
                    }
                    configurationUnavailable = true;
                }
                return;
            }

            String content = new String(java.nio.file.Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
            String hash = configHash(content);

            if (hash.equals(lastConfigHash) && !configurationUnavailable) {
                return; // Ничего не изменилось
            }

            log.info("Обнаружено изменение tenants.json (хеш: {} -> {}), перечитываем конфиг", lastConfigHash, hash);
            List<TenantConfig> newTenants = objectMapper.readValue(content, new TypeReference<>() {});
            syncTenants(newTenants);
            lastConfigHash = hash;
            configurationUnavailable = false;

        } catch (Exception e) {
            readinessRegistry.markConfigurationFailure();
            configurationUnavailable = true;
            log.error("Ошибка при проверке конфига тенантов: errorType={}", e.getClass().getSimpleName());
            log.debug("Технические детали синхронизации конфига тенантов", e);
        }
    }

    /**
     * Обновляет хеш конфига после ручного обновления Secret с этого же пода.
     */
    public void refreshHash() {
        tenantLifecycleService.executeSerialized(this::refreshHashSerialized);
    }

    private void refreshHashSerialized() {
        try {
            File file = new File(tenantsConfigPath);
            if (file.exists()) {
                String content = new String(java.nio.file.Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
                lastConfigHash = configHash(content);
            } else if (tenantsConfigRequired) {
                readinessRegistry.markConfigurationFailure();
                configurationUnavailable = true;
            }
        } catch (Exception e) {
            readinessRegistry.markConfigurationFailure();
            configurationUnavailable = true;
            log.warn("Не удалось обновить хеш конфига тенантов: errorType={}",
                    e.getClass().getSimpleName());
            log.debug("Технические детали обновления хеша тенантов", e);
        }
    }

    static String configHash(String content) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            return HexFormat.of().formatHex(digest.digest(content.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("SHA-256 недоступен в текущем окружении", e);
        }
    }

    /**
     * Синхронизирует in-memory тенантов с конфигом из файла.
     */
    private void syncTenants(List<TenantConfig> newTenants) {
        tenantLifecycleService.synchronizeFromPersistedConfig(newTenants);
    }

}
