package ervu.service.scheduler;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import javax.annotation.PostConstruct;

import com.fasterxml.jackson.databind.ObjectMapper;
import ervu.client.okopf.EsnsiOkopfClient;
import ervu.dao.okopf.OkopfDao;
import ervu.model.okopf.OkopfAttributeValueModel;
import ervu.model.okopf.OkopfDataModel;
import ervu.model.okopf.OkopfModel;
import ervu.model.okopf.OkopfOrgModel;
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static org.springframework.scheduling.config.ScheduledTaskRegistrar.CRON_DISABLED;
import static org.springframework.util.StringUtils.hasText;


/**
 * @author Artyom Hackimullin
 */
@Service
@DependsOn({"liquibase"})
public class EsnsiOkopfSchedulerServiceImpl implements EsnsiOkopfSchedulerService {
  private static final Logger LOGGER = LoggerFactory.getLogger(
      EsnsiOkopfSchedulerServiceImpl.class);

  @Autowired
  private EsnsiOkopfClient esnsiOkopfClient;
  @Autowired
  private OkopfDao okopfDao;
  @Autowired
  private ObjectMapper mapper;
  @Value("${esnsi.okopf.cron.load:0 0 */1 * * *}")
  private String cronLoad;

  @PostConstruct
  @Transactional
  public void init() {
    if (!cronLoad.equals(CRON_DISABLED)) {
      LOGGER.info("Synchronization with esnsi okopf enabled");
      load();
    }
    else {
      LOGGER.info("Synchronization with esnsi okopf disabled");
    }
  }

  @Scheduled(cron = "${esnsi.okopf.cron.load:0 0 */1 * * *}")
  @SchedulerLock(name = "loadOkopf")
  @Transactional
  public void load() {
    LOGGER.info("Loading okopf file");
    try {
      String data = esnsiOkopfClient.getJsonOkopFormData();
      if (hasText(data)) {
        LOGGER.info("Parsing from json file to okopf model");
        OkopfOrgModel orgModel = mapper.readValue(data, OkopfOrgModel.class);
        int currentVersion = mapper.readTree(data).findValue("version").asInt();
        List<OkopfModel> okopfRecords = mapToOkopfRecords(orgModel.getData(), currentVersion);
        LOGGER.info("Finished parsing from json file to okopf model");
        LOGGER.info("Loaded {} okopf records with version {}. Beginning to save okopf data",  okopfRecords.size(), currentVersion);
        okopfDao.saveOrUpdate(okopfRecords);
        LOGGER.info("Successfully saved okopf data");
      } else {
        LOGGER.info("Data is empty");
      }
    }
    catch (Exception e) {
      LOGGER.error("Failed to load okopf data", e);
    }
  }

  private List<OkopfModel> mapToOkopfRecords(OkopfDataModel dataModel, int version) {
    return Arrays.stream(dataModel.getDetails())
        .flatMap(detail -> {
          OkopfAttributeValueModel[] attributeValues = detail.getAttributeValues();
          String key = attributeValues[0].getValue();
          String value = attributeValues[1].getValue();
          return Stream.of(new OkopfModel(key, value, version));
        })
        .toList();
  }
}
