package io.gmss.fiscad.implementations.infocad.parametre;

import io.gmss.fiscad.entities.decoupage.Arrondissement;
import io.gmss.fiscad.entities.decoupage.Quartier;
import io.gmss.fiscad.entities.infocad.parametre.Bloc;
import io.gmss.fiscad.entities.infocad.parametre.Structure;
import io.gmss.fiscad.exceptions.BadRequestException;
import io.gmss.fiscad.exceptions.NotFoundException;
import io.gmss.fiscad.interfaces.decoupage.ArrondissementService;
import io.gmss.fiscad.interfaces.infocad.parametre.BlocService;
import io.gmss.fiscad.interfaces.infocad.parametre.StructureService;
import io.gmss.fiscad.repositories.infocad.parametre.BlocRepository;
import io.gmss.fiscad.service.StringService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class BlocServiceImpl implements BlocService {

    private final BlocRepository blocRepository;
    private final ArrondissementService arrondissementService;
    private final StringService stringService;
    private final StructureService structureService;

    public BlocServiceImpl(BlocRepository blocRepository, ArrondissementService arrondissementService, StringService stringService, StructureService structureService) {
        this.blocRepository = blocRepository;
        this.arrondissementService = arrondissementService;
        this.stringService = stringService;
        this.structureService = structureService;
    }

    @Override
    public Bloc createBloc(Bloc bloc) throws BadRequestException {
        if (bloc.getId() != null) {
            throw new BadRequestException("Impossible de créer un nouveau bloc ayant un id non null.");
        }
        String idBlocParArrondissement = getIdBlocParArrondissementValue(bloc);
        bloc.setIdBlocParArrondissement(idBlocParArrondissement);

        if (bloc.getStructure() == null) {
            bloc.setCote(getCoteValue(bloc, idBlocParArrondissement, null));
        } else {
            bloc.setCote(getCoteValue(bloc, idBlocParArrondissement, bloc.getStructure().getId()));
        }

        if (bloc.getQuartier() != null) {
            bloc.setCoteq(getCoteQuartierByCoteBloc(bloc.getCote(), bloc.getQuartier()));
        }
        if (bloc.getSecteurDecoupage() != null) {
            bloc.setSecteur(bloc.getSecteurDecoupage().getSecteur());
           // bloc.setSecteurDecoupage();
        }


        return blocRepository.save(bloc);
    }

    private String getCoteQuartierByCoteBloc(String cote, Quartier quartier) {
        if (cote == null) {
            throw new BadRequestException("Le bloc spécifié ne dispose pas encore de code.");
        } else {
            return new StringBuilder().append(cote.substring(0, 3)).append(quartier.getCode()).toString();
        }
    }

    private String getCoteValue(Bloc bloc, String idBlocParArrondissement, Long structure_id) {
        StringBuilder builder = new StringBuilder();

        if (structure_id == null) {
            structure_id = blocRepository.getStructureId(bloc.getArrondissement().getId());
            if (structure_id == null) {
                throw new NotFoundException("Aucune structure n'est associé à ce bloc. Il n'est donc pas possible d'enregistrer ce bloc. Vous devez donc attribué ce bloc à une structure.");
            }
        }

        Optional<Structure> structureOptional = structureService.getStructureById(structure_id);
        if (structureOptional.isPresent()) {
            builder.append(structureOptional.get().getCode());
            builder.append(bloc.getArrondissement().getCode());
            builder.append(idBlocParArrondissement);
            return builder.toString();
        } else {
            throw new NotFoundException("Impossible de trouver la structure associée à ce bloc");
        }
    }

    private String getIdBlocParArrondissementValue(Bloc bloc) {
        int a = countAllBlocByArrondissement(bloc.getArrondissement().getId()) + 1;
        return stringService.generateIntInTwoPositions(a);
    }

    @Override
    public Bloc updateBloc(Long id, Bloc bloc) throws NotFoundException {
        if (bloc.getId() == null) {
            throw new BadRequestException("Impossible de mettre à jour un nouveau bloc ayant un id null.");
        }
        if (!blocRepository.existsById(bloc.getId())) {
            throw new NotFoundException("Impossible de trouver le bloc spécifié dans notre base de données.");
        }
        String idBlocParArrondissement = getIdBlocParArrondissementValue(bloc);
        bloc.setIdBlocParArrondissement(idBlocParArrondissement);

        if (bloc.getStructure() == null) {
            bloc.setCote(getCoteValue(bloc, idBlocParArrondissement, null));
        } else {
            bloc.setCote(getCoteValue(bloc, idBlocParArrondissement, bloc.getStructure().getId()));
        }

        if (bloc.getQuartier() != null) {
            bloc.setCoteq(getCoteQuartierByCoteBloc(bloc.getCote(), bloc.getQuartier()));
        }
        return blocRepository.save(bloc);
    }

    @Override
    public void deleteBloc(Long id) throws NotFoundException {
        Optional<Bloc> blocOptional = blocRepository.findById(id);
        if (blocOptional.isPresent()) {
            blocRepository.deleteById(blocOptional.get().getId());
        } else {
            throw new NotFoundException("Impossible de trouver le bloc spécifié dans notre base de données.");
        }
    }

    @Override
    public Page<Bloc> getBlocList(Pageable pageable) {
        return blocRepository.findAll(pageable);
    }

    @Override
    public List<Bloc> getBlocsByArrondissement(Long idArrondissement) {
        Arrondissement arrondissement = arrondissementService.getArrondissementById(idArrondissement).orElseThrow(() -> {
            throw new NotFoundException("Impossible de trouver l'arrondissement");
        });
        return blocRepository.findAllByArrondissement(arrondissement);
    }

    @Override
    public List<Bloc> getBlocsByStructure(Long idStructure) {
        if (structureService.getStructureById(idStructure).isPresent()) {
            return blocRepository.getBlocsByStructure(idStructure);
        } else {
            throw new NotFoundException("Impossible de trouver la structure spécifiée.");
        }
    }

    @Override
    public List<Bloc> getBlocsBySecteur(Long idSecteur) {
        return blocRepository.findAllBySecteur_Id(idSecteur);
    }

    @Override
    public List<Bloc> getBlocList() {
        return blocRepository.findAll();
    }

    @Override
    public Optional<Bloc> getBlocById(Long id) {
        if (blocRepository.existsById(id)) {
            return blocRepository.findById(id);
        } else {
            throw new NotFoundException("Impossible de trouver le bloc spécifié dans la base de données.");
        }
    }

    @Override
    public int countAllBlocByArrondissement(Long idArrondissement) {
        if (arrondissementService.getArrondissementById(idArrondissement).isPresent()) {
            return blocRepository.countAllBlocByArrondissement(idArrondissement);
        } else {
            throw new NotFoundException("Impossible de trouver l'arrondissement' spécifié dans la base de données.");
        }
    }
}
