package it.sw.pa.comune.artegna.web.rest;

import it.sw.pa.comune.artegna.domain.User;
import it.sw.pa.comune.artegna.repository.PrenotazioneRepository;
import it.sw.pa.comune.artegna.security.AuthoritiesConstants;
import it.sw.pa.comune.artegna.service.PrenotazioneQueryService;
import it.sw.pa.comune.artegna.service.PrenotazioneService;
import it.sw.pa.comune.artegna.service.UserService;
import it.sw.pa.comune.artegna.service.UtenteAppService;
import it.sw.pa.comune.artegna.service.criteria.PrenotazioneCriteria;
import it.sw.pa.comune.artegna.service.dto.PrenotazioneDTO;
import it.sw.pa.comune.artegna.service.dto.UserDTO;
import it.sw.pa.comune.artegna.service.dto.UtenteAppDTO;
import it.sw.pa.comune.artegna.web.rest.errors.BadRequestAlertException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import tech.jhipster.service.filter.LongFilter;
import tech.jhipster.web.util.HeaderUtil;
import tech.jhipster.web.util.PaginationUtil;
import tech.jhipster.web.util.ResponseUtil;

/**
 * REST controller for managing {@link it.sw.pa.comune.artegna.domain.Prenotazione}.
 */
@RestController
@RequestMapping("/api/prenotaziones")
public class PrenotazioneResource {

    private static final Logger LOG = LoggerFactory.getLogger(PrenotazioneResource.class);

    private static final String ENTITY_NAME = "prenotazione";

    @Value("${jhipster.clientApp.name:smartbooking}")
    private String applicationName;

    private final PrenotazioneService prenotazioneService;

    private final PrenotazioneRepository prenotazioneRepository;

    private final PrenotazioneQueryService prenotazioneQueryService;

    private final UserService userService;
    private final UtenteAppService utenteAppService;

    public PrenotazioneResource(
        PrenotazioneService prenotazioneService,
        PrenotazioneRepository prenotazioneRepository,
        PrenotazioneQueryService prenotazioneQueryService,
        UserService userService,
        UtenteAppService utenteAppService
    ) {
        this.prenotazioneService = prenotazioneService;
        this.prenotazioneRepository = prenotazioneRepository;
        this.prenotazioneQueryService = prenotazioneQueryService;
        this.userService = userService;
        this.utenteAppService = utenteAppService;
    }

    /**
     * {@code POST  /prenotaziones} : Create a new prenotazione.
     *
     * @param prenotazioneDTO the prenotazioneDTO to create.
     * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new prenotazioneDTO, or with status {@code 400 (Bad Request)} if the prenotazione has already an ID.
     * @throws URISyntaxException if the Location URI syntax is incorrect.
     */
    @PostMapping("")
    public ResponseEntity<PrenotazioneDTO> createPrenotazione(@RequestBody PrenotazioneDTO prenotazioneDTO) throws URISyntaxException {
        LOG.debug("REST request to save Prenotazione : {}", prenotazioneDTO);
        if (prenotazioneDTO.getId() != null) {
            throw new BadRequestAlertException("A new prenotazione cannot already have an ID", ENTITY_NAME, "idexists");
        }
        prenotazioneDTO = prenotazioneService.save(prenotazioneDTO);
        return ResponseEntity.created(new URI("/api/prenotaziones/" + prenotazioneDTO.getId()))
            .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, prenotazioneDTO.getId().toString()))
            .body(prenotazioneDTO);
    }

    /**
     * {@code PUT  /prenotaziones/:id} : Updates an existing prenotazione.
     *
     * @param id the id of the prenotazioneDTO to save.
     * @param prenotazioneDTO the prenotazioneDTO to update.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated prenotazioneDTO,
     * or with status {@code 400 (Bad Request)} if the prenotazioneDTO is not valid,
     * or with status {@code 500 (Internal Server Error)} if the prenotazioneDTO couldn't be updated.
     * @throws URISyntaxException if the Location URI syntax is incorrect.
     */
    @PutMapping("/{id}")
    public ResponseEntity<PrenotazioneDTO> updatePrenotazione(
        @PathVariable(value = "id", required = false) final Long id,
        @RequestBody PrenotazioneDTO prenotazioneDTO
    ) throws URISyntaxException {
        LOG.debug("REST request to update Prenotazione : {}, {}", id, prenotazioneDTO);
        if (prenotazioneDTO.getId() == null) {
            throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
        }
        if (!Objects.equals(id, prenotazioneDTO.getId())) {
            throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid");
        }

        if (!prenotazioneRepository.existsById(id)) {
            throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound");
        }

        prenotazioneDTO = prenotazioneService.update(prenotazioneDTO);
        return ResponseEntity.ok()
            .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, prenotazioneDTO.getId().toString()))
            .body(prenotazioneDTO);
    }

    /**
     * {@code PATCH  /prenotaziones/:id} : Partial updates given fields of an existing prenotazione, field will ignore if it is null
     *
     * @param id the id of the prenotazioneDTO to save.
     * @param prenotazioneDTO the prenotazioneDTO to update.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated prenotazioneDTO,
     * or with status {@code 400 (Bad Request)} if the prenotazioneDTO is not valid,
     * or with status {@code 404 (Not Found)} if the prenotazioneDTO is not found,
     * or with status {@code 500 (Internal Server Error)} if the prenotazioneDTO couldn't be updated.
     * @throws URISyntaxException if the Location URI syntax is incorrect.
     */
    @PatchMapping(value = "/{id}", consumes = { "application/json", "application/merge-patch+json" })
    public ResponseEntity<PrenotazioneDTO> partialUpdatePrenotazione(
        @PathVariable(value = "id", required = false) final Long id,
        @RequestBody PrenotazioneDTO prenotazioneDTO
    ) throws URISyntaxException {
        LOG.debug("REST request to partial update Prenotazione partially : {}, {}", id, prenotazioneDTO);
        if (prenotazioneDTO.getId() == null) {
            throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
        }
        if (!Objects.equals(id, prenotazioneDTO.getId())) {
            throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid");
        }

        if (!prenotazioneRepository.existsById(id)) {
            throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound");
        }

        Optional<PrenotazioneDTO> result = prenotazioneService.partialUpdate(prenotazioneDTO);

        return ResponseUtil.wrapOrNotFound(
            result,
            HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, prenotazioneDTO.getId().toString())
        );
    }

    /**
     * {@code GET  /prenotaziones} : get all the prenotaziones.
     *
     * @param pageable the pagination information.
     * @param criteria the criteria which the requested entities should match.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of prenotaziones in body.
     */
    @GetMapping("")
    public ResponseEntity<List<PrenotazioneDTO>> getAllPrenotaziones(
        PrenotazioneCriteria criteria,
        @org.springdoc.core.annotations.ParameterObject Pageable pageable,
        @AuthenticationPrincipal UserDetails principal
    ) {
        LOG.debug("REST request to get Prenotaziones by criteria: {}", criteria);

        String currentUser = principal.getUsername();
        User user = userService.getUserWithAuthoritiesByLogin(currentUser).orElseThrow();

        // switch among user authorities
        return switch (user.getAuthorities().stream().findFirst().orElseThrow().getName()) {
            case AuthoritiesConstants.INCARICATO, AuthoritiesConstants.ADMIN -> ResponseEntity.ok().body(
                prenotazioneQueryService.findByCriteria(criteria, pageable).getContent()
            );
            default -> {
                UtenteAppDTO utenteAppDTO = utenteAppService.fetchUtenteAppFromUser();
                criteria.setUtenteId(new LongFilter());
                criteria.getUtenteId().setEquals(utenteAppDTO.getId());
                yield ResponseEntity.ok().body(prenotazioneQueryService.findByCriteria(criteria, pageable).getContent());
            }
        };
    }

    /**
     * {@code GET  /prenotaziones/count} : count all the prenotaziones.
     *
     * @param criteria the criteria which the requested entities should match.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the count in body.
     */
    @GetMapping("/count")
    public ResponseEntity<Long> countPrenotaziones(PrenotazioneCriteria criteria) {
        LOG.debug("REST request to count Prenotaziones by criteria: {}", criteria);
        return ResponseEntity.ok().body(prenotazioneQueryService.countByCriteria(criteria));
    }

    /**
     * {@code GET  /prenotaziones/:id} : get the "id" prenotazione.
     *
     * @param id the id of the prenotazioneDTO to retrieve.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the prenotazioneDTO, or with status {@code 404 (Not Found)}.
     */
    @GetMapping("/{id}")
    public ResponseEntity<PrenotazioneDTO> getPrenotazione(@PathVariable("id") Long id) {
        LOG.debug("REST request to get Prenotazione : {}", id);
        Optional<PrenotazioneDTO> prenotazioneDTO = prenotazioneService.findOne(id);
        return ResponseUtil.wrapOrNotFound(prenotazioneDTO);
    }

    /**
     * {@code DELETE  /prenotaziones/:id} : delete the "id" prenotazione.
     *
     * @param id the id of the prenotazioneDTO to delete.
     * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deletePrenotazione(@PathVariable("id") Long id) {
        LOG.debug("REST request to delete Prenotazione : {}", id);
        prenotazioneService.delete(id);
        return ResponseEntity.noContent()
            .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString()))
            .build();
    }
}
