package vn.azteam.tpf.web.rest;

import com.codahale.metrics.annotation.Timed;
import io.github.jhipster.web.util.ResponseUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.web.bind.annotation.*;
import vn.azteam.tpf.domain.TBCodeDetailsStatusEnum;
import vn.azteam.tpf.domain.TBCodeStatusEnum;
import vn.azteam.tpf.service.*;
import vn.azteam.tpf.service.dto.*;
import vn.azteam.tpf.service.util.PageableUtil;
import vn.azteam.tpf.service.util.UserRoleUtil;
import vn.azteam.tpf.web.rest.errors.BadRequestAlertException;
import vn.azteam.tpf.web.rest.util.HeaderUtil;
import vn.azteam.tpf.web.rest.util.PaginationUtil;
import vn.azteam.tpf.web.rest.util.RandomStringUtil;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api")
public class TBCodeResource {

    private static final String ENTITY_NAME = "tBCode";

    private final Logger log = LoggerFactory.getLogger(TBCodeResource.class);

    private final TBCodeService tBCodeService;

    private final TBCodeDetailsService tbCodeDetailsService;

    private final TBCropService tBCropService;

    private final UserService userService;

    private final TBCodeQueryService tBCodeQueryService;

    private final UserRoleUtil userRoleUtil;

    private final PageableUtil pageableUtil;

    public TBCodeResource(TBCodeService tBCodeService, TBCodeDetailsService tbCodeDetailsService, TBCropService tBCropService, UserService userService, TBCodeQueryService tBCodeQueryService, UserRoleUtil userRoleUtil, PageableUtil pageableUtil) {
        this.tBCodeService = tBCodeService;
        this.tbCodeDetailsService = tbCodeDetailsService;
        this.tBCropService = tBCropService;
        this.userService = userService;
        this.tBCodeQueryService = tBCodeQueryService;
        this.userRoleUtil = userRoleUtil;
        this.pageableUtil = pageableUtil;
    }


    /**
     * GET  /tb-codes-dropdown-list : get all the tBCode for dropdown options.
     *
     * @param criteria the criterias which the requested entities should match
     * @return the ResponseEntity with status 200 (OK) and the list of tBCode in body
     */
    @GetMapping("/tb-codes/dropdown-list")
    @Timed
    public ResponseEntity<List<TBCodeDTO>> getAllTBCodesForDropdown(TBCodeCriteria criteria) {
        log.debug("REST request to get TBCodes by criteria: {}", criteria);
        Page<TBCodeDTO> page = tBCodeQueryService.findByCriteria(criteria, Pageable.unpaged());

        //Authorize get list crop by customer of current user
        List<TBCodeDTO> result = page.getContent().stream()
            .filter(item -> userRoleUtil.currentUserHasPermissionByCustomerId(item.getId())
                && userRoleUtil.currentUserHasPermissionByCropId(item.getId()))
            .sorted(Comparator.comparing(
                TBCodeDTO::getCreatedDate,
                Comparator.nullsFirst(Comparator.naturalOrder())).reversed())
            .collect(Collectors.toList());

        return ResponseEntity.ok().headers(null).body(result);
    }

    /**
     * GET  /tb-codes : get all the tBCodes.
     *
     * @param pageable the pagination information
     * @param criteria the criterias which the requested entities should match
     * @return the ResponseEntity with status 200 (OK) and the list of tBCrops in body
     */
    @GetMapping("/tb-codes")
    @Timed
    public ResponseEntity<List<TBCodeDTO>> getAllTBCodes(TBCodeCriteria criteria, Pageable pageable) {
        log.debug("REST request to get TBCrops by criteria: {}", criteria);
        Page<TBCodeDTO> page = tBCodeQueryService.findByCriteria(criteria, Pageable.unpaged());

        //Authorize get list crop by customer of current user
        List<TBCodeDTO> result = page.getContent().stream()
            .filter(item -> userRoleUtil.currentUserHasPermissionByCustomerId(item.getId())
                && userRoleUtil.currentUserHasPermissionByCropId(item.getId()))
            .sorted(Comparator.comparing(
                TBCodeDTO::getCreatedDate,
                Comparator.nullsFirst(Comparator.naturalOrder())).reversed())
            .collect(Collectors.toList());

        Page<TBCodeDTO> pageResult = pageableUtil.changeTBCodeDTOToPageFromList(result, pageable);
        for (TBCodeDTO itemResult : result) {
            //logger.error("Start date: " + itemResult.getStartDate());
        }
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(pageResult, "/api/tb-codes");
        return ResponseEntity.ok().headers(headers).body(pageResult.getContent());
    }

    @PostMapping("/tb-codes")
    @Timed
    public ResponseEntity<TBCodeDTO> createTBCrop(@RequestBody TBCodeCreationDTO tBCodeCreationDTO) throws URISyntaxException {
        log.debug("REST request to save TBCode : {}", tBCodeCreationDTO);
        //Set created by to current user login
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        if (tBCodeCreationDTO.gettBCropId() != null) {
            throw new BadRequestAlertException("1047", ENTITY_NAME, "1047");
        }
        if (tBCodeCreationDTO.getQuantity() != null && tBCodeCreationDTO.getQuantity() <= 0) {
            throw new BadRequestAlertException("1047", ENTITY_NAME, "1047");
        }

        Optional<TBCropDTO> tbCropDTO = tBCropService.findOne(tBCodeCreationDTO.gettBCropId());
        if (!tbCropDTO.isPresent()) {
            throw new BadRequestAlertException("1019", ENTITY_NAME, "1019");
        }

        try {
            TBCodeDTO tBCodeDTO = new TBCodeDTO();
            tBCodeDTO.setTbCrop(tbCropDTO.get());
            tBCodeDTO.setDescription(StringUtils.isNotBlank(tBCodeCreationDTO.getDescription()) ?
                tBCodeCreationDTO.getDescription() : "");
            tBCodeDTO.setQuantity(tBCodeCreationDTO.getQuantity());
            tBCodeDTO.setPathImage(StringUtils.isNotBlank(tBCodeCreationDTO.getPathImage()) ?
                tBCodeCreationDTO.getPathImage() : "");
            tBCodeDTO.setCreatedDate(Instant.now());
            tBCodeDTO.setCreatedById(currentUser.getUserId());
            tBCodeDTO.setStatus(TBCodeStatusEnum.NEW);
            tBCodeDTO.setExpiredDate(tBCodeCreationDTO.getExpiredDate());
            TBCodeDTO result = null;
            Boolean hasViolationException = false;
            do {
                try {
                    tBCodeDTO.setCode(RandomStringUtil.generateRandomStringFromUUID(8));
                    result = tBCodeService.save(tBCodeDTO);
                } catch (org.springframework.dao.DataIntegrityViolationException ex) {
                    if (ex.getMessage().contains("ux_tb_code_code")) {
                        hasViolationException = true;
                    } else {
                        throw ex;
                    }
                }
            }
            while (hasViolationException);

            if (result.getId() != null) {
                List<TBCodeDetailsDTO> tbCodeDetailsDTOS = new ArrayList<>();
                for (int i = 1; i <= tBCodeCreationDTO.getQuantity(); i++) {
                    TBCodeDetailsDTO codeDetailsDTO = new TBCodeDetailsDTO();
                    codeDetailsDTO.setCode(tBCodeDTO.getCode());
                    codeDetailsDTO.setTbCode(tBCodeDTO);
                    codeDetailsDTO.setNumberScan(0);
                    codeDetailsDTO.setStatus(TBCodeDetailsStatusEnum.NEW);
                    codeDetailsDTO.setCreatedDate(Instant.now());
                    tBCodeDTO.setCreatedById(currentUser.getUserId());
                    tbCodeDetailsDTOS.add(codeDetailsDTO);
                }
                tbCodeDetailsService.saveAll(tbCodeDetailsDTOS);
            }

            return ResponseEntity.created(new URI("/api/tb-codes/" + result.getId()))
                .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
                .body(result);
        } catch (Exception exception) {
            throw new RuntimeException();
        }
    }

    @GetMapping("/tb-codes/{id}")
    @Timed
    public ResponseEntity<TBCodeDTO> getTBCodes(@PathVariable Long id) {
        log.debug("REST request to get TBCode : {}", id);
        Optional<TBCodeDTO> tBCropDTO = tBCodeService.findOne(id);
        //@CUONGLT - Filter scope customer's data
//        if (!userRoleUtil.currentUserHasPermissionByCropId(id)) {
//            throw new BadRequestAlertException("1018", ENTITY_NAME, "1018");
//        }
        return ResponseUtil.wrapOrNotFound(tBCropDTO);
    }

    /**
     * Add or delete crop's person in charges
     *
     * @param cropPersonInChargeDTO
     * @return
     */
    @PutMapping("/tb-codes/update/{id}/status/{status}")
    @Timed
    public ResponseEntity<TBCodeDTO> updateTBCode(@PathVariable Long id, String status) {
        log.debug("REST request to update tb codes : {}, status {} ", id, status);
        if (id == null || StringUtils.isBlank(status)) {
            throw new BadRequestAlertException("1039", ENTITY_NAME, "1039");
        }
        try {
            TBCodeStatusEnum.valueOf(status);
        } catch (Exception exception) {
            throw new BadRequestAlertException("1039", ENTITY_NAME, "1039");
        }
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        TBCodeDTO tbCodeDTO = new TBCodeDTO();
        tbCodeDTO.setId(id);
        tbCodeDTO.setModifiedDate(Instant.now());
        tbCodeDTO.setModifiedById(currentUser.getId());
        tbCodeDTO.setStatus(TBCodeStatusEnum.valueOf(status));

        Optional<TBCodeDTO> tBCodeUpdated = tBCodeService.updateStatusTBCode(tbCodeDTO);
//        if (null != tBCropDTO) {
//            sendNotificationPersonInCharge(cropPersonInChargeDTO, listAssignmentPrev);
//        }
        return ResponseUtil.wrapOrNotFound(tBCodeUpdated);
    }
}
