package org.micord.utils;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import org.micord.enums.ConfigType;
import org.micord.models.CachedConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Maksim Tereshin
 */
@Component
public class ConfigLoader {

  private static final Logger LOGGER = Logger.getLogger(ConfigLoader.class.getName());
  private static final Map<String, CachedConfig> cachedConfigs = new ConcurrentHashMap<>();

  @Value("${configDirectory}")
  private String configDirectory;


  public <T> Optional<T> loadConfigIfModified(ConfigType configType, Class<T> configClass) {
    String methodName = configType.getType();
    String fileName = methodName + ".xml";

    if (configDirectory == null) {
      LOGGER.log(Level.SEVERE, "No configuration directory found for method: " + methodName);
      return Optional.empty();
    }

    try {
      File configFile = new File(configDirectory + File.separator + fileName);
      Path configFilePath = configFile.toPath();
      FileTime currentModifiedTime = Files.getLastModifiedTime(configFilePath);
      CachedConfig cachedConfig = cachedConfigs.getOrDefault(methodName, null);

      if (cachedConfig == null || !currentModifiedTime.equals(cachedConfig.getModifiedTime())) {
        // Load the updated configuration
        JAXBContext jaxbContext = JAXBContext.newInstance(configClass);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        T loadedConfig = unmarshalConfig(configFile, configClass);
        cachedConfigs.put(methodName, new CachedConfig(loadedConfig, currentModifiedTime));
        return Optional.of(loadedConfig);
      }
      else {
        return (Optional<T>) Optional.of(cachedConfigs.get(methodName).getConfig());
      }

    }
    catch (IOException e) {
      LOGGER.log(Level.SEVERE, "Failed to load configuration file: " + fileName, e);
      return Optional.empty();  // Return empty if there is an IO error
    }
    catch (JAXBException e) {
      LOGGER.log(Level.SEVERE, "Failed to unmarshal configuration file: " + fileName, e);
      return Optional.empty();  // Return empty if unmarshalling fails
    }
  }

  private <T> T unmarshalConfig(File configFile, Class<T> configClass) throws JAXBException {
    JAXBContext jaxbContext = JAXBContext.newInstance(configClass);
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    return configClass.cast(unmarshaller.unmarshal(configFile));
  }
}
