package com.sn.sowsysrestapi.domain.service;

import com.sn.sowsysrestapi.api.converter.ReportDirectoryConverter;
import com.sn.sowsysrestapi.api.dto.ReportDirectoryDTO;
import com.sn.sowsysrestapi.domain.entity.ReportDirectory;
import com.sn.sowsysrestapi.domain.entity.User;
import com.sn.sowsysrestapi.domain.exception.BusinessException;
import com.sn.sowsysrestapi.domain.exception.EntityInUseException;
import com.sn.sowsysrestapi.domain.exception.ReportDirectoryNotFoundException;
import com.sn.sowsysrestapi.domain.repository.ReportDirectoryRepo;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Service
public class ReportDirectoryService {

    public static final String MSG_ENTITY_IN_USE = "Report Directory with ID %d can't be deleted. " +
            "Reason: Other entity is using this entity.";

    private static final String MSG_OWNER_ID_ALREADY_IN_DIRECTORY = "Error in trying to assign an Owner ID to the " +
            "Report Directory. Reason: The Owner ID is already registered on the Report Directory table.";

    @Autowired
    private ReportDirectoryRepo reportDirectoryRepo;

    @Autowired
    private ReportDirectoryConverter reportDirectoryConverter;

    @Autowired
    private UserService userService;

    @Autowired
    private ModelMapper modelMapper;

    @Transactional
    public ReportDirectory save(ReportDirectory reportDirectory) {

        try {
            return reportDirectoryRepo.save(reportDirectory);
        } catch (ConstraintViolationException | DataIntegrityViolationException e) {
            throw new BusinessException(MSG_OWNER_ID_ALREADY_IN_DIRECTORY);
        }
    }

    @Transactional
    public ReportDirectory update(ReportDirectory reportDirectory) {

        reportDirectory.setOwnerCode(reportDirectory.getOwnerCode());

        return reportDirectoryRepo.save(reportDirectory);

    }

    @Transactional
    public void addSupervisorIdToList(String userCode, String supervisorCode) {

        userService.findOrFail(userCode);
        userService.findOrFail(supervisorCode);

        ReportDirectory reportDirectory = reportDirectoryRepo.findByOwnerCode(userCode)
                .orElseThrow(() -> new ReportDirectoryNotFoundException(userCode));
        Set<String> tempSupervisorCodeList = reportDirectory.getSupervisorCode();

        tempSupervisorCodeList.add(supervisorCode);

        reportDirectory.setSupervisorCode(tempSupervisorCodeList);

    }

    @Transactional
    public void removeSupervisorIdFromList(String userCode, String supervisorCode) {

        userService.findOrFail(userCode);
        userService.findOrFail(supervisorCode);

        ReportDirectory reportDirectory = reportDirectoryRepo.findByOwnerCode(userCode)
                .orElseThrow(() -> new ReportDirectoryNotFoundException(userCode));
        Set<String> tempSupervisorCodeList = reportDirectory.getSupervisorCode();

        tempSupervisorCodeList.remove(supervisorCode);

        reportDirectory.setSupervisorCode(tempSupervisorCodeList);

    }

    public void delete(String userCode) {

        try {
            reportDirectoryRepo.deleteByOwnerCode(userCode);
            reportDirectoryRepo.flush();

        } catch (EmptyResultDataAccessException e) {
            throw new ReportDirectoryNotFoundException(userCode);

        } catch (DataIntegrityViolationException e) {
            throw new EntityInUseException(
                    String.format(MSG_ENTITY_IN_USE, userCode));
        }

    }

    public ReportDirectory findOrFail(String userCode) {
        return reportDirectoryRepo.findByOwnerCode(userCode)
                .orElseThrow(() -> new ReportDirectoryNotFoundException(userCode));
    }

    public ReportDirectory findOrFail(Long userCode) {
        return reportDirectoryRepo.findByOwnerId(userCode);
        // .orElseThrow(() -> new ReportDirectoryNotFoundException(userCode));
    }

    public ReportDirectory findOrFailByOwnerCode(String ownerCode) {

        ReportDirectory reportDirectory = reportDirectoryRepo.findByOwnerCode(ownerCode)
                .orElseThrow(() -> new ReportDirectoryNotFoundException(ownerCode));

        if (reportDirectory == null) {
            throw new BusinessException(
                    String.format("There is no Report Directory with the user owner code: %s.", ownerCode)
            );
        }

        return reportDirectory;
    }

    public List<ReportDirectory> findOrFailBySupervisorCode(String ownerCode, String supervisorCode) {

        User user = userService.findOrFail(ownerCode);

        List<ReportDirectory> reportDirectoryList = reportDirectoryRepo.findBySupervisorCode(supervisorCode);

        if (reportDirectoryList.isEmpty()) {
            throw new BusinessException(
                    String.format("There is no Report Directory associated with the supervisor code: %s.", supervisorCode)
            );
        }

        return reportDirectoryList;
    }

    public List<ReportDirectory> findOrFailByOwnerIdAndSupervisorCode(String userCode, String supervisorCode) {

        User user = userService.findOrFail(userCode);

        Long userPresentId = user.getId();
        String userPresentUsername = user.getUsername();

        if (reportDirectoryRepo.findByOwnerCodeAndSupervisorCode(userCode, supervisorCode).isEmpty()) {
            throw new BusinessException(
                    String.format("The user:  %s don't have access to this Report Directory.", userPresentUsername)
            );
        }

        List<ReportDirectory> reportDirectoryList = reportDirectoryRepo.findBySupervisorCode(supervisorCode);

        if (reportDirectoryList.isEmpty()) {
            throw new BusinessException(
                    String.format("There is no Report Directory associated with the supervisor code: %s.", supervisorCode)
            );
        }

        return reportDirectoryList;
    }


}
