package vn.azteam.tpf.web.rest;

import com.codahale.metrics.annotation.Timed;
import io.github.jhipster.service.filter.IntegerFilter;
import io.github.jhipster.service.filter.LongFilter;
import vn.azteam.tpf.service.TBCropQueryService;
import vn.azteam.tpf.service.TBEntityService;
import vn.azteam.tpf.service.UserService;
import vn.azteam.tpf.service.dto.*;
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.service.TBEntityQueryService;
import io.github.jhipster.web.util.ResponseUtil;
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.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.net.URI;
import java.net.URISyntaxException;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * REST controller for managing TBEntity.
 */
@RestController
@RequestMapping("/api")
public class TBEntityResource {

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

    private static final String ENTITY_NAME = "tBEntity";

    private static final int LEVEL_AREA = Integer.valueOf(0);

    private static final int LEVEL_NET_HOUSE = Integer.valueOf(1);

    private static final Long ENTITY_TYPE_NET_HOUSE = Long.valueOf(2);

    private final TBEntityService tBEntityService;

    private final TBEntityQueryService tBEntityQueryService;

    private final UserService userService;

    private final TBCropQueryService tBCropQueryService;

    public TBEntityResource(
        TBEntityService tBEntityService,
        TBEntityQueryService tBEntityQueryService,
        UserService userService,
        TBCropQueryService tBCropQueryService) {
        this.tBEntityService = tBEntityService;
        this.tBEntityQueryService = tBEntityQueryService;
        this.userService = userService;
        this.tBCropQueryService = tBCropQueryService;
    }

    /**
     * POST  /tb-entities : Create a new tBEntity.
     *
     * @param tBEntityDTO the tBEntityDTO to create
     * @return the ResponseEntity with status 201 (Created) and with body the new tBEntityDTO, or with status 400 (Bad Request) if the tBEntity has already an ID
     * @throws URISyntaxException if the Location URI syntax is incorrect
     */
    @PostMapping("/tb-entities")
    @Timed
    public ResponseEntity<TBEntityDTO> createTBEntity(@RequestBody TBEntityDTO tBEntityDTO) throws URISyntaxException {
        log.debug("REST request to save TBEntity : {}", tBEntityDTO);
        if (tBEntityDTO.getId() != null) {
            throw new BadRequestAlertException("A new tBEntity cannot already have an ID", ENTITY_NAME, "idexists");
        }
        if(tBEntityDTO.getTbCustomerId() == null){
            //Đơn vị không tồn tại
            throw new BadRequestAlertException("1054", ENTITY_NAME, "1054");
        }
        //Set created date to current timestampc
        tBEntityDTO.setCreatedDate(Instant.now());
        //Set created by to current user login
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        tBEntityDTO.setCreatedById(currentUser.getUserId());
        if (tBEntityDTO.getTbCustomerId() == null) {
            tBEntityDTO.setTbCustomerId(currentUser.getCustomerId());
        }

        TBEntityDTO result = tBEntityService.save(tBEntityDTO);
        return ResponseEntity.created(new URI("/api/tb-entities/" + result.getId()))
            .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
            .body(result);
    }

    /**
     * PUT  /tb-entities : Updates an existing tBEntity.
     *
     * @param tBEntityDTO the tBEntityDTO to update
     * @return the ResponseEntity with status 200 (OK) and with body the updated tBEntityDTO,
     * or with status 400 (Bad Request) if the tBEntityDTO is not valid,
     * or with status 500 (Internal Server Error) if the tBEntityDTO couldn't be updated
     * @throws URISyntaxException if the Location URI syntax is incorrect
     */
    @PutMapping("/tb-entities")
    @Timed
    public ResponseEntity<TBEntityDTO> updateTBEntity(@RequestBody TBEntityDTO tBEntityDTO) throws URISyntaxException {
        log.debug("REST request to update TBEntity : {}", tBEntityDTO);
        if (tBEntityDTO.getId() == null) {
            throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
        }

        //Set created date to current timestamp
        tBEntityDTO.setModifiedDate(Instant.now());
        //Set created by to current user login
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        tBEntityDTO.setModifiedById(currentUser.getUserId());

        TBEntityDTO result = tBEntityService.save(tBEntityDTO);
        return ResponseEntity.ok()
            .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, tBEntityDTO.getId().toString()))
            .body(result);
    }

    /**
     * GET  /tb-entities : get all the tBEntities.
     *
     * @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 tBEntities in body
     */
    @GetMapping("/tb-entities")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getAllTBEntities(TBEntityCriteria criteria, Pageable pageable) {
        log.debug("REST request to get TBEntities by criteria: {}", criteria);
        Page<TBEntityDTO> page = tBEntityQueryService.findByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/tb-entities");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

     /**
     * GET  /tb-entities : get all the tBEntities.
     *
     * @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 tBEntities in body
     */
    @GetMapping("/tb-warehouses")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getAllTBWarehouseEntities(TBEntityCriteria criteria, Pageable pageable) {
        log.debug("REST request to get TBEntities by criteria: {}", criteria);
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        LongFilter customerIdFilter = new LongFilter();
        customerIdFilter.setEquals(currentUser.getCustomerId());
        criteria.setTbCustomerId(customerIdFilter);
        Page<TBEntityDTO> page = tBEntityQueryService.findWarehouseByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/tb-entities");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    /**
     * GET  /tb-entities/customer/:customerId : get the "id" tBEntity.
     *
     * @param customerId the id of the tBCustomerDTO to retrieve
     * @return the ResponseEntity with status 200 (OK) and the list of tBEntities in body
     */
    @GetMapping("/tb-entities-by-customer-id/{customerId}")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getListTBEntitiesByCustomerId(@PathVariable("customerId") Long customerId, TBEntityCriteria criteria, Pageable pageable) {
        log.debug("REST request to get TBEntities by customer Id: {}", customerId);

        LongFilter customerFilter = new LongFilter();
        customerFilter.setEquals(customerId);
        criteria.setTbCustomerId(customerFilter);

        Page<TBEntityDTO> page = tBEntityQueryService.findByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/tb-entities");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    @GetMapping("/tb-entities/area")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getAllAreas(TBEntityCriteria criteria, Pageable pageable) {
        log.debug("REST request to get Areas by criteria: {}", criteria);
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        IntegerFilter levelFilter = new IntegerFilter();
        levelFilter.setEquals(LEVEL_AREA);
        criteria.setLevel(levelFilter);
        if (currentUser.getCustomerId() != null) {
            LongFilter customerFilter = new LongFilter();
            customerFilter.setEquals(currentUser.getCustomerId());
            criteria.setTbCustomerId(customerFilter);
        }
        Page<TBEntityDTO> page = tBEntityQueryService.findByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/tb-entities/area");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    @GetMapping("/tb-entities-by-customer/area/{customerId}")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getAllAreasByCustomer(@PathVariable Long customerId, Pageable pageable) {
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        TBEntityCriteria criteria = new TBEntityCriteria();
        IntegerFilter levelFilter = new IntegerFilter();
        levelFilter.setEquals(LEVEL_AREA);
        criteria.setLevel(levelFilter);
        if (currentUser.getCustomerId() != null && !currentUser.getCustomerId().equals(customerId)) {
            return ResponseEntity.ok().headers(null).body(null);
        }
        LongFilter customerFilter = new LongFilter();
        customerFilter.setEquals(customerId);
        criteria.setTbCustomerId(customerFilter);

        Page<TBEntityDTO> page = tBEntityQueryService.findByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/tb-entities/area");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    @GetMapping("/tb-entities/net-houses/{areaId}")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getNetHousesByArea(@PathVariable("areaId") Long areaId, TBEntityCriteria criteria, Pageable pageable) {
        log.debug("REST request to get NetHouses by criteria: {}", criteria);
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        IntegerFilter levelFilter = new IntegerFilter();
        levelFilter.setEquals(LEVEL_NET_HOUSE);
        criteria.setLevel(levelFilter);

        LongFilter areaFilter = new LongFilter();
        areaFilter.setEquals(areaId);
        criteria.setParentId(areaFilter);
        if (currentUser.getCustomerId() != null){
            LongFilter customerFilter = new LongFilter();
            customerFilter.setEquals(currentUser.getCustomerId());
            criteria.setTbCustomerId(customerFilter);
        }
        Page<TBEntityDTO> page = tBEntityQueryService.findByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/tb-entities/net-houses");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    @GetMapping("/tb-entities/all-net-houses")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getAllNetHouses(TBEntityCriteria criteria, Pageable pageable) {
        log.debug("REST request to get NetHouses by criteria: {}", criteria);
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        LongFilter tbEntityTypeFilter = new LongFilter();
        tbEntityTypeFilter.setEquals(ENTITY_TYPE_NET_HOUSE);
        criteria.setTbEntityTypeId(tbEntityTypeFilter);

        if(currentUser.getCustomerId() != null){
            LongFilter customerFilter = new LongFilter();
            customerFilter.setEquals(currentUser.getCustomerId());
            criteria.setTbCustomerId(customerFilter);
        }
        Page<TBEntityDTO> page = tBEntityQueryService.findByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/tb-entities/net-houses");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    /**
     * GET  /tb-entities/count : count all the tBEntities.
     *
     * @param criteria the criterias which the requested entities should match
     * @return the ResponseEntity with status 200 (OK) and the count in body
     */
    @GetMapping("/tb-entities/count")
    @Timed
    public ResponseEntity<Long> countTBEntities(TBEntityCriteria criteria) {
        log.debug("REST request to count TBEntities by criteria: {}", criteria);
        return ResponseEntity.ok().body(tBEntityQueryService.countByCriteria(criteria));
    }

    /**
     * GET  /tb-entities/:id : get the "id" tBEntity.
     *
     * @param id the id of the tBEntityDTO to retrieve
     * @return the ResponseEntity with status 200 (OK) and with body the tBEntityDTO, or with status 404 (Not Found)
     */
    @GetMapping("/tb-entities/{id}")
    @Timed
    public ResponseEntity<TBEntityDTO> getTBEntity(@PathVariable Long id) {
        log.debug("REST request to get TBEntity : {}", id);
        Optional<TBEntityDTO> tBEntityDTO = tBEntityService.findOne(id);
        return ResponseUtil.wrapOrNotFound(tBEntityDTO);
    }

    /**
     * GET  /tb-entities/customer/:customerId : get the "id" tBEntity.
     *
     * @param customerId the id of the tBCustomerDTO to retrieve
     * @return the ResponseEntity with status 200 (OK) and the list of tBEntities in body
     */
    @GetMapping("/tb-entities/customer/{customerId}")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> getTBEntitiesByCustomerId(@PathVariable("customerId") Long customerId, TBEntityCriteria criteria, Pageable pageable) {
        log.debug("REST request to get TBEntities by customer Id: {}", customerId);

        LongFilter customerFilter = new LongFilter();
        customerFilter.setEquals(customerId);
        criteria.setTbCustomerId(customerFilter);

        IntegerFilter levelFilter = new IntegerFilter();
        levelFilter.setEquals(LEVEL_NET_HOUSE);
        criteria.setLevel(levelFilter);

        Page<TBEntityDTO> page = tBEntityQueryService.findByCriteria(criteria, pageable);
        TBCropCriteria cropCriteria = new TBCropCriteria();
        List<TBCropDTO> cropDTOS = tBCropQueryService.findByCriteria(cropCriteria);
        List<Object[]> activitiesOfNetHouses = tBEntityQueryService.getAllActivitiesOfNetHouses();
        List<TBEntityDTO> tbEntityDTOs = new ArrayList<>();

        for (TBEntityDTO tbEntityDTO : page.getContent()) {
            List<TBDetailUserDTO> userDTOs = tBEntityQueryService.getListUsersInChargeOf(tbEntityDTO, cropDTOS);
            Integer color = tBEntityQueryService.getEntityColor(tbEntityDTO, activitiesOfNetHouses);
            tbEntityDTO.setDetailUserDTOS(userDTOs);
            tbEntityDTO.setColor(color);

            // Get list crop by entity id
            TBCropCriteria tbCropCriteria = new TBCropCriteria();
            LongFilter tbEntityFilter = new LongFilter();
            tbEntityFilter.setEquals(tbEntityDTO.getId());
            tbCropCriteria.setNetHouseId(tbEntityFilter);

            Page<TBCropDTO> pageTBCrop = tBCropQueryService.findByCriteria(tbCropCriteria, pageable);
            List<TBCropDTO> newTBCropDTOs = tBCropQueryService.updateListToHasGuidelinePeriod(pageTBCrop.getContent());
            tbEntityDTO.setTbCropDTOS(newTBCropDTOs);
            tbEntityDTOs.add(tbEntityDTO);
        }

        log.debug("REST request to get TBEntities by criteria: {}", criteria);
        return ResponseEntity.ok().headers(null).body(tbEntityDTOs);
    }

    /**
     * DELETE  /tb-entities/:id : delete the "id" tBEntity.
     *
     * @param id the id of the tBEntityDTO to delete
     * @return the ResponseEntity with status 200 (OK)
     */
    @DeleteMapping("/tb-entities/{id}")
    @Timed
    public ResponseEntity<Void> deleteTBEntity(@PathVariable Long id) {
        log.debug("REST request to delete TBEntity : {}", id);
        tBEntityService.delete(id);
        return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
    }

    /**
     * SEARCH  /_search/tb-entities?query=:query : search for the tBEntity corresponding
     * to the query.
     *
     * @param query    the query of the tBEntity search
     * @param pageable the pagination information
     * @return the result of the search
     */
    @GetMapping("/_search/tb-entities")
    @Timed
    public ResponseEntity<List<TBEntityDTO>> searchTBEntities(@RequestParam String query, Pageable pageable) {
        log.debug("REST request to search for a page of TBEntities for query {}", query);
        Page<TBEntityDTO> page = tBEntityService.search(query, pageable);
        HttpHeaders headers = PaginationUtil.generateSearchPaginationHttpHeaders(query, page, "/api/_search/tb-entities");
        return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
    }

   @GetMapping("/unit/{unitType}")
    @Timed
    public ResponseEntity<List<Object>> getDetailSupplies(@PathVariable String unitType) {
        List<Object> result = tBEntityQueryService.getUnits(unitType);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

}
