package ru.micord.ervu.account_applications.service.processor.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.stereotype.Component;
import ru.micord.ervu.account_applications.dao.IpDirectoryDao;
import ru.micord.ervu.account_applications.dao.RecruitmentIpDao;
import ru.micord.ervu.account_applications.db_beans.public_.tables.records.IpDirectoryRecord;
import ru.micord.ervu.account_applications.db_beans.public_.tables.records.RecruitmentIpRecord;
import ru.micord.ervu.account_applications.model.RecruitmentIpData;
import ru.micord.ervu.account_applications.service.processor.LinkDataProcessor;

/**
 * @author Adel Kalimullin
 */
@Component
public class RecruitmentIpProcessor
    implements LinkDataProcessor<RecruitmentIpData, RecruitmentIpRecord> {
  private final RecruitmentIpDao recruitmentIpDao;
  private final IpDirectoryDao ipDirectoryDao;

  public RecruitmentIpProcessor(RecruitmentIpDao recruitmentIpDao, IpDirectoryDao ipDirectoryDao) {
    this.recruitmentIpDao = recruitmentIpDao;
    this.ipDirectoryDao = ipDirectoryDao;
  }

  @Override
  public void upsertData(RecruitmentIpData data) {
    RecruitmentIpRecord record = mapToRecord(data);
    recruitmentIpDao.upsertData(record);

    String ip = data.getIpAddress();
    boolean exists = ipDirectoryDao.exists(ip);
    if (!exists) {
      IpDirectoryRecord ipDirectoryRecord = ipDirectoryDao.newRecord();
      ipDirectoryRecord.setIpAddress(ip);
      ipDirectoryDao.upsertData(ipDirectoryRecord);
    }
  }

  @Override
  public void deleteLink(RecruitmentIpData data) {
    RecruitmentIpRecord record = mapToRecord(data);
    recruitmentIpDao.deleteRecruitmentIp(record);

    String ip = data.getIpAddress();
    boolean stillUsed = recruitmentIpDao.existsIpAddress(ip);
    if (!stillUsed) {
      ipDirectoryDao.deleteByIpAddresses(List.of(ip));
    }
  }

  @Override
  public void changeActiveStatus(String id, boolean active) {
    throw new UnsupportedOperationException("Not supported yet.");
  }

  @Override
  public Class<RecruitmentIpData> getType() {
    return RecruitmentIpData.class;
  }

  @Override
  public RecruitmentIpRecord mapToRecord(RecruitmentIpData data) {
    RecruitmentIpRecord record = recruitmentIpDao.newRecord();
    record.setRecruitmentId(data.getIdDomain());
    record.setIpAddress(data.getIpAddress());
    return record;
  }

  public void upsertRecruitmentIpAddresses(String recruitmentId, Set<String> incomingIps) {
    Set<String> existingIps = recruitmentIpDao.getIpAddressesByRecruitmentId(recruitmentId);

    Set<String> toAdd = new HashSet<>(incomingIps);
    toAdd.removeAll(existingIps);

    Set<String> toDelete = new HashSet<>(existingIps);
    toDelete.removeAll(incomingIps);

    handleDeletion(recruitmentId, toDelete);
    handleInsertion(recruitmentId, toAdd);
  }

  private void handleDeletion(String recruitmentId, Set<String> toDelete) {
    if (toDelete.isEmpty()) {
      return;
    }

    recruitmentIpDao.deleteRecruitmentIpsByRecruitmentIdAndIps(recruitmentId, List.copyOf(toDelete));

    Set<String> stillUsedIps = recruitmentIpDao.getExistingIpAddresses(List.copyOf(toDelete));
    Set<String> toRemoveFromDirectory = toDelete.stream()
        .filter(ip -> !stillUsedIps.contains(ip))
        .collect(Collectors.toSet());

    if (!toRemoveFromDirectory.isEmpty()) {
      ipDirectoryDao.deleteByIpAddresses(List.copyOf(toRemoveFromDirectory));
    }
  }

  private void handleInsertion(String recruitmentId, Set<String> toAdd) {
    if (toAdd.isEmpty()) {
      return;
    }

    List<RecruitmentIpRecord> recordsToAdd = toAdd.stream()
        .map(ip -> mapToRecord(new RecruitmentIpData(recruitmentId, ip)))
        .toList();
    recruitmentIpDao.mergeRecords(recordsToAdd);

    Set<String> existingDirectoryIps = ipDirectoryDao.getExistingIpAddresses(List.copyOf(toAdd));
    Set<String> directoryToInsert = toAdd.stream()
        .filter(ip -> !existingDirectoryIps.contains(ip))
        .collect(Collectors.toSet());

    if (!directoryToInsert.isEmpty()) {
      List<IpDirectoryRecord> ipDirectoryRecords = directoryToInsert.stream()
          .map(ip -> {
            IpDirectoryRecord record = ipDirectoryDao.newRecord();
            record.setIpAddress(ip);
            return record;
          })
          .toList();
      ipDirectoryDao.mergeRecords(ipDirectoryRecords);
    }
  }
}
