package it.micegroup.gestioneferie.pianificazioneferie.controller;

import lombok.RequiredArgsConstructor;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DpianificFerieAmmto;
import it.micegroup.gestioneferie.pianificazioneferie.assembler.DpianificFerieAmmtoModelAssembler;
import it.micegroup.gestioneferie.pianificazioneferie.criteria.DpianificFerieAmmtoCriteria;
import it.micegroup.gestioneferie.pianificazioneferie.service.DpianificFerieAmmtoService;
import it.micegroup.gestioneferie.pianificazioneferie.service.DrichPianifFerieAmmtoService;
import it.micegroup.gestioneferie.pianificazioneferie.service.DperiodiAmmtoPfService;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DrichPianifFerieAmmto;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DperiodiAmmtoPf;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DstatiPianificFerie;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DesitiRichAdesionePf;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DstatiPianificFerieKey;

import it.micegroup.gestioneferie.pianificazioneferie.entity.DesitiRichAdesionePfKey;

import it.micegroup.voila2runtime.exception.*;
import it.micegroup.gestioneferie.libs.controller.BaseController;
import it.micegroup.gestioneferie.pianificazioneferie.dto.DpianificFerieAmmtoDto;
import it.micegroup.gestioneferie.pianificazioneferie.dto.DpianificFerieAmmtoWriteDto;
import it.micegroup.gestioneferie.libs.response.BaseResponse;
import it.micegroup.gestioneferie.libs.request.BaseRequest;
import javax.annotation.PostConstruct;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.PagedModel;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.validation.Valid;
import java.net.URI;
import java.util.Locale;
import java.util.Optional;

import java.util.Collection;
import org.modelmapper.PropertyMap;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/dpianific-ferie-ammto", produces = MediaType.APPLICATION_JSON_VALUE)
public class DpianificFerieAmmtoController extends BaseController<BaseResponse<DpianificFerieAmmtoDto>> {

	/// ENTITY SERVICE
	private final DpianificFerieAmmtoService dpianificFerieAmmtoService;
	private final DpianificFerieAmmtoModelAssembler dpianificFerieAmmtoModelAssembler;
	private final PagedResourcesAssembler<DpianificFerieAmmto> pagedResourcesAssembler;
	private final ModelMapper modelMapper;
	// CHILD SERVICES
	private final DrichPianifFerieAmmtoService drichPianifFerieAmmtoService;
	private final DperiodiAmmtoPfService dperiodiAmmtoPfService;

	// CONSTRUCTOR
	@PostConstruct
	public void postConstruct() {
		modelMapper.addMappings(new PropertyMap<DpianificFerieAmmtoWriteDto, DpianificFerieAmmto>() {
			@Override
			protected void configure() {
				skip(destination.getTheDrichPianifFerieAmmto());
				skip(destination.getTheDperiodiAmmtoPf());
			}
		});
	}

	@SuppressWarnings("unused")
	private static final Logger LOGGER = LogManager.getLogger(DpianificFerieAmmtoController.class);

	// API
	@GetMapping
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_FIND_PAGINATED.toString())")
	public ResponseEntity<BaseResponse<PagedModel<DpianificFerieAmmtoDto>>> findPaginated(
			DpianificFerieAmmtoCriteria criteria, Pageable pageable) {
		Page<DpianificFerieAmmto> page = dpianificFerieAmmtoService.findByCriteria(criteria, pageable);
		return toResponseEntityPaged(page, null);
	}

	/**
	 * {@code GET  /dpianific-ferie-ammto/:objectKey} : get the
	 * dpianific-ferie-ammto with given objectKey.
	 *
	 * @param objectKey the objectKey of the dpianific-ferie-ammto to retrieve.
	 * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body
	 *         the dpianific-ferie-ammto, or with status {@code 404 (Not Found)}.
	 */
	@GetMapping("/{objectKey:.+}")
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_FIND_BY_ID.toString())")
	public ResponseEntity<BaseResponse<DpianificFerieAmmtoDto>> getByObjectKey(@PathVariable String objectKey) {
		return toResponseEntity(dpianificFerieAmmtoService.findByObjectKey(objectKey), null, HttpStatus.OK);
	}

	/**
	 * {@code PUT  /dpianific-ferie-ammto} : Updates an existing
	 * DpianificFerieAmmto.
	 *
	 * @param DpianificFerieAmmto the DpianificFerieAmmto to update.
	 * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body
	 *         the updated DpianificFerieAmmto, or with status
	 *         {@code 400 (Bad Request)} if the DpianificFerieAmmto is not valid, or
	 *         with status {@code 500 (Internal Server Error)} if the
	 *         DpianificFerieAmmto couldn't be updated.
	 */
	@PutMapping
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_UPDATE.toString())")
	public ResponseEntity<?> update(@Valid @RequestBody BaseRequest<DpianificFerieAmmtoWriteDto> request) {
		return toResponseEntity(
				Optional.of(dpianificFerieAmmtoService
						.bulkUpdate(modelMapper.map(request.getData(), DpianificFerieAmmto.class))),
				null, HttpStatus.OK);
	}

	/**
	 * {@code POST  /dpianific-ferie-ammto} : Create a new DpianificFerieAmmto.
	 *
	 * @param DpianificFerieAmmto the DpianificFerieAmmto to create.
	 * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with
	 *         body the new DpianificFerieAmmto, or with status
	 *         {@code 400 (Bad Request)} if the DpianificFerieAmmto has already an
	 *         ID.
	 * @throws URISyntaxException if the Location URI syntax is incorrect.
	 */
	@PostMapping
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_CREATE.toString())")
	public ResponseEntity<?> save(@Valid @RequestBody BaseRequest<DpianificFerieAmmtoWriteDto> request) {
		ResponseEntity<?> response = null;
		DpianificFerieAmmto dpianificFerieAmmtoToInsert = childMap(request.getData());
		try {
			DpianificFerieAmmto newDpianificFerieAmmto = dpianificFerieAmmtoService
					.insert(modelMapper.map(dpianificFerieAmmtoToInsert, DpianificFerieAmmto.class));
			URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
					.buildAndExpand(newDpianificFerieAmmto.getDpianificFerieAmmtoKey()).toUri();
			HttpHeaders headers = new HttpHeaders();
			headers.setLocation(location);
			response = toResponseEntity(Optional.of(newDpianificFerieAmmto), headers, HttpStatus.CREATED);
		} catch (Exception exception) {
			LOGGER.error("Exception:", exception);
		}
		return response;
	}

	private DpianificFerieAmmto childMap(DpianificFerieAmmtoWriteDto data) {
		DpianificFerieAmmto dpianificFerieAmmtoToInsert = modelMapper.map(data, DpianificFerieAmmto.class);
		Collection<DrichPianifFerieAmmto> theDrichPianifFerieAmmtos = data.getTheDrichPianifFerieAmmto().stream()
				.map(element -> modelMapper.map(element, DrichPianifFerieAmmto.class))
				.peek(element -> element.setTheDpianificFerieAmmto(dpianificFerieAmmtoToInsert))
				.collect(Collectors.toList());
		dpianificFerieAmmtoToInsert.setTheDrichPianifFerieAmmto(theDrichPianifFerieAmmtos);
		Collection<DperiodiAmmtoPf> theDperiodiAmmtoPfs = data.getTheDperiodiAmmtoPf().stream()
				.map(element -> modelMapper.map(element, DperiodiAmmtoPf.class))
				.peek(element -> element.setTheDpianificFerieAmmto(dpianificFerieAmmtoToInsert))
				.collect(Collectors.toList());
		dpianificFerieAmmtoToInsert.setTheDperiodiAmmtoPf(theDperiodiAmmtoPfs);
		return dpianificFerieAmmtoToInsert;
	}

	/**
	 * {@code DELETE  /dpianific-ferie-ammto/:objectKey} : delete the
	 * dpianific-ferie-ammto with given objectKey.
	 *
	 * @param objectKey the objectKey of the DpianificFerieAmmto to delete.
	 * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
	 */
	@DeleteMapping("/{objectKey:.+}")
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_DELETE.toString())")
	public ResponseEntity<?> delete(@PathVariable String objectKey) {
		dpianificFerieAmmtoService
				.deleteById(dpianificFerieAmmtoService.findByObjectKey(objectKey).get().getDpianificFerieAmmtoKey());
		return new ResponseEntity<>(new BaseResponse<>(objectKey), HttpStatus.OK);
	}

	/**
	 * GET /the-dstati-pianific-ferie_id: Search all DpianificFerieAmmto for the
	 * given DstatiPianificFerie (parent)
	 * 
	 * @param OK       of DstatiPianificFerie
	 * @param pageable
	 * @return Page of DpianificFerieAmmto for the given DstatiPianificFerie
	 *         (parent)
	 */
	@GetMapping("/findByTheDstatiPianificFerie/{dstatiPianificFerieObjectKey:.+}")
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_FIND_BY_DSTATI_PIANIFIC_FERIE.toString())")
	public ResponseEntity<BaseResponse<PagedModel<DpianificFerieAmmtoDto>>> findByDstatiPianificFerie(
			@PathVariable String dstatiPianificFerieObjectKey, Pageable pageable) {
		DstatiPianificFerie key = new DstatiPianificFerie(dstatiPianificFerieObjectKey);
		return toResponseEntityPaged(dpianificFerieAmmtoService.findByTheDstatiPianificFerie(key, pageable), null);
	}

	/**
	 * GET /the-desiti-rich-adesione-pf_id: Search all DpianificFerieAmmto for the
	 * given DesitiRichAdesionePf (parent)
	 * 
	 * @param OK       of DesitiRichAdesionePf
	 * @param pageable
	 * @return Page of DpianificFerieAmmto for the given DesitiRichAdesionePf
	 *         (parent)
	 */
	@GetMapping("/findByTheDesitiRichAdesionePf/{desitiRichAdesionePfObjectKey:.+}")
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_FIND_BY_DESITI_RICH_ADESIONE_PF.toString())")
	public ResponseEntity<BaseResponse<PagedModel<DpianificFerieAmmtoDto>>> findByDesitiRichAdesionePf(
			@PathVariable String desitiRichAdesionePfObjectKey, Pageable pageable) {
		DesitiRichAdesionePf key = new DesitiRichAdesionePf(desitiRichAdesionePfObjectKey);
		return toResponseEntityPaged(dpianificFerieAmmtoService.findByTheDesitiRichAdesionePf(key, pageable), null);
	}

	/**
	 * GET /id/drich-pianif-ferie-ammto: Search all DrichPianifFerieAmmto (childs)
	 * for the given DpianificFerieAmmto
	 * 
	 * @param objectKey ObjectKey of DpianificFerieAmmto
	 * @param pageable
	 * @return Page of DrichPianifFerieAmmto for the given DpianificFerieAmmto
	 */
	@GetMapping("/{objectKey:.+}/drich-pianif-ferie-ammto")
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_FIND_BY_THE_DRICH_PIANIF_FERIE_AMMTO_OBJECT_KEY.toString())")
	public ResponseEntity<Page<DrichPianifFerieAmmto>> getTheDrichPianifFerieAmmtoByObjectKey(
			@PathVariable String objectKey, Pageable pageable) {
		DpianificFerieAmmto dpianificFerieAmmto = new DpianificFerieAmmto();
		dpianificFerieAmmto.setObjectKey(objectKey);
		return ResponseEntity.ok()
				.body(drichPianifFerieAmmtoService.findByTheDpianificFerieAmmto(dpianificFerieAmmto, pageable));
	}

	/**
	 * GET /id/dperiodi-ammto-pf: Search all DperiodiAmmtoPf (childs) for the given
	 * DpianificFerieAmmto
	 * 
	 * @param objectKey ObjectKey of DpianificFerieAmmto
	 * @param pageable
	 * @return Page of DperiodiAmmtoPf for the given DpianificFerieAmmto
	 */
	@GetMapping("/{objectKey:.+}/dperiodi-ammto-pf")
	@PreAuthorize("hasRole(@permissionHolder.DPIANIFIC_FERIE_AMMTO_FIND_BY_THE_DPERIODI_AMMTO_PF_OBJECT_KEY.toString())")
	public ResponseEntity<Page<DperiodiAmmtoPf>> getTheDperiodiAmmtoPfByObjectKey(@PathVariable String objectKey,
			Pageable pageable) {
		DpianificFerieAmmto dpianificFerieAmmto = new DpianificFerieAmmto();
		dpianificFerieAmmto.setObjectKey(objectKey);
		return ResponseEntity.ok()
				.body(dperiodiAmmtoPfService.findByTheDpianificFerieAmmto(dpianificFerieAmmto, pageable));
	}

	private ResponseEntity<BaseResponse<DpianificFerieAmmtoDto>> toResponseEntity(
			Optional<DpianificFerieAmmto> maybeResponse, HttpHeaders header, HttpStatus status) {
		return maybeResponse
				.map(response -> new ResponseEntity<>(
						new BaseResponse<>(dpianificFerieAmmtoModelAssembler.toModel(response)), header, status))
				.orElseThrow(() -> new NotFoundException(DpianificFerieAmmto.class, new Locale("it")));
	}

	private ResponseEntity<BaseResponse<PagedModel<DpianificFerieAmmtoDto>>> toResponseEntityPaged(
			Page<DpianificFerieAmmto> list, HttpHeaders header) {
		BaseResponse<PagedModel<DpianificFerieAmmtoDto>> collModel = new BaseResponse<>(
				pagedResourcesAssembler.toModel(list, dpianificFerieAmmtoModelAssembler));
		return ResponseEntity.ok().headers(header).body(collModel);
	}

	@SuppressWarnings("unused")
	private DpianificFerieAmmto dtoToEntity(DpianificFerieAmmtoDto dto) {
		return new ModelMapper().map(dto, DpianificFerieAmmto.class);
	}
}
