package com.abm.mainet.rnl.service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.abm.mainet.cfc.challan.dto.ChallanReceiptPrintDTO;
import com.abm.mainet.cfc.challan.service.IChallanService;
import com.abm.mainet.common.constant.MainetConstants;
import com.abm.mainet.common.constant.PrefixConstants;
import com.abm.mainet.common.domain.LocationMasEntity;
import com.abm.mainet.common.domain.Organisation;
import com.abm.mainet.common.dto.ApplicantDetailDTO;
import com.abm.mainet.common.dto.CommonChallanDTO;
import com.abm.mainet.common.dto.WardZoneBlockDTO;
import com.abm.mainet.common.integration.dms.service.IFileUploadService;
import com.abm.mainet.common.master.repository.TbCfcApplicationAddressJpaRepository;
import com.abm.mainet.common.master.repository.TbCfcApplicationMstJpaRepository;
import com.abm.mainet.common.service.ApplicationService;
import com.abm.mainet.common.service.IOrganisationService;
import com.abm.mainet.common.utility.ApplicationContextProvider;
import com.abm.mainet.common.utility.CommonMasterUtility;
import com.abm.mainet.common.utility.LookUp;
import com.abm.mainet.common.utility.SeqGenFunctionUtility;
import com.abm.mainet.common.utility.UtilityService;
import com.abm.mainet.rnl.domain.EstateBooking;
import com.abm.mainet.rnl.domain.EstateEntity;
import com.abm.mainet.rnl.domain.EstatePropertyEntity;
import com.abm.mainet.rnl.dto.BookingReqDTO;
import com.abm.mainet.rnl.dto.BookingResDTO;
import com.abm.mainet.rnl.dto.EstateBookingDTO;
import com.abm.mainet.rnl.dto.EstatePropResponseDTO;
import com.abm.mainet.rnl.dto.PropFreezeDTO;
import com.abm.mainet.rnl.dto.PropInfoDTO;
import com.abm.mainet.rnl.dto.PropertyResDTO;
import com.abm.mainet.rnl.repository.EstateBookingRepository;
import com.abm.mainet.rnl.repository.EstatePropertyRepository;

/**
 * @author ritesh.patil
 *
 */
@Service
@Repository
public class EstateBookingServiceImpl implements IEstateBookingService {

    @Autowired
    private EstatePropertyRepository estatePropertyRepository;

    @Autowired
    private ApplicationService applicationService;

    @Autowired
    private IChallanService iChallanService;

    @Resource
    private IFileUploadService fileUploadService;

    @Autowired
    private EstateBookingRepository estateBookingRepository;

    @Autowired
    private IOrganisationService iOrganisationService;

    @Autowired
    private SeqGenFunctionUtility seqGenFunctionUtility;

    private static Logger logger = Logger.getLogger(EstateBookingServiceImpl.class);

    @Override
    @Transactional(readOnly = true)
    public PropertyResDTO getAllRentedProperties(final String subCategoryName, final String prefixName, final Long orgId,
            final Integer subCategoryType) {
        EstatePropResponseDTO estatePropResponseDTO = null;
        List<EstatePropResponseDTO> estatePropResponseDTOs = null;
        final PropertyResDTO propResponseDTO = new PropertyResDTO();
        List<EstatePropertyEntity> list = null;
        if (null == subCategoryType) {
            list = estatePropertyRepository.getAllRentedProperties(subCategoryName, prefixName, orgId);
        } else {
            list = estatePropertyRepository.getFilteredRentedProperties(subCategoryName, prefixName, orgId, subCategoryType);
        }
        if (list != null) {
            estatePropResponseDTOs = new ArrayList<>();
            for (final EstatePropertyEntity estatePropertyEntity : list) {
                estatePropResponseDTO = new EstatePropResponseDTO();
                estatePropResponseDTO.setPropId(estatePropertyEntity.getPropId());
                estatePropResponseDTO.setPropName(estatePropertyEntity.getName());
                estatePropResponseDTO.setPropertyNo(estatePropertyEntity.getCode());
                final EstateEntity estateEntity = estatePropertyEntity.getEstateEntity();
                estatePropResponseDTO.setCategory(findCategoryName(estateEntity.getType2(), orgId));
                final LocationMasEntity locationMasEntity = estateEntity.getLocationMas();
                estatePropResponseDTO.setLocation(locationMasEntity.getLocAddress() + " " + locationMasEntity.getPincode());
                estatePropResponseDTOs.add(estatePropResponseDTO);
            }

            propResponseDTO.setEstatePropResponseDTOs(estatePropResponseDTOs);
        }
        return propResponseDTO;
    }

    private String findCategoryName(final long subCategoryId, final Long orgId) {
        String categoryName = StringUtils.EMPTY;
        final List<LookUp> lookUps = CommonMasterUtility.getNextLevelData(PrefixConstants.CATEGORY_PREFIX_NAME,
                MainetConstants.EstateBooking.LEVEL, orgId);
        if ((lookUps != null) && !lookUps.isEmpty()) {
            for (final LookUp lookUp : lookUps) {
                if (lookUp.getLookUpId() == subCategoryId) {
                    categoryName = lookUp.getDescLangFirst();
                    break;
                }
            }
        }
        return categoryName == null ? "" : categoryName;
    }

    @Override
    @Transactional
    public BookingResDTO saveEstateBooking(final BookingReqDTO bookingReqDTO, final CommonChallanDTO offline,
            final String serviceName) {
        setRequestApplicantDetails(bookingReqDTO);
        final EstateBookingDTO estateBookingDTO = bookingReqDTO.getEstateBookingDTO();
        bookingReqDTO.setOrgId(estateBookingDTO.getOrgId());
        bookingReqDTO.setLangId(estateBookingDTO.getLangId());
        bookingReqDTO.setUserId(estateBookingDTO.getCreatedBy());
        final Long applicationId = applicationService.createApplication(bookingReqDTO);
        ChallanReceiptPrintDTO printDto = null;
        if (offline != null) {
            offline.setApplNo(applicationId);
            printDto = iChallanService.savePayAtUlbCounter(offline, serviceName);
        }
        final Long javaFq = seqGenFunctionUtility.generateSequenceNo(MainetConstants.RnLCommon.RentLease,
                MainetConstants.EstateMaster.TB_RL_ESTATE_MAS, MainetConstants.EstateMaster.ES_CODE, estateBookingDTO.getOrgId(),
                MainetConstants.RnLCommon.Flag_C, null);
        EstateBooking estateBooking = new EstateBooking();
        BeanUtils.copyProperties(estateBookingDTO, estateBooking);
        estateBooking.setApplicationId(applicationId);
        estateBooking.setBookingNo(javaFq.toString());
        estateBooking.setBookingDate(new Date());
        final EstatePropertyEntity estatePropertyEntity = new EstatePropertyEntity();
        estatePropertyEntity.setPropId(bookingReqDTO.getEstatePropResponseDTO().getPropId());
        estateBooking.setEstatePropertyEntity(estatePropertyEntity);
        estateBooking.setAmount(bookingReqDTO.getPayAmount());
        estateBooking.setSecurityAmount(0.0); // HArd Code
        estateBooking.setTermAccepted(true);
        estateBooking.setPayFlag(true);
        estateBooking.setBookingStatus(MainetConstants.RnLCommon.Y_FLAG);
        estateBooking = estateBookingRepository.save(estateBooking);
        estateBookingRepository.updateAppliactionMst(estateBooking.getApplicationId());
        final BookingResDTO bookingResDTO = new BookingResDTO();
        if (offline != null) {
            bookingResDTO.setChallanReceiptPrintDTO(printDto);
        }
        bookingResDTO.setBookingNo(estateBooking.getBookingNo());
        bookingResDTO.setApplicationNo(estateBooking.getApplicationId());
        bookingResDTO.setStatus("success");
        bookingReqDTO.setApplicationId(applicationId);
        if ((bookingReqDTO.getDocumentList() != null) && !bookingReqDTO.getDocumentList().isEmpty()) {
            fileUploadService.doFileUpload(bookingReqDTO.getDocumentList(), bookingReqDTO);
        }
        return bookingResDTO;
    }

    private void setRequestApplicantDetails(final BookingReqDTO reqDTO) {
        final ApplicantDetailDTO appDTO = reqDTO.getApplicantDetailDto();
        final Organisation organisation = new Organisation();
        final Long orgId = reqDTO.getEstateBookingDTO().getOrgId();
        organisation.setOrgid(orgId);
        reqDTO.setmName(appDTO.getApplicantMiddleName());
        reqDTO.setfName(appDTO.getApplicantFirstName());
        reqDTO.setlName(appDTO.getApplicantLastName());
        reqDTO.setEmail(appDTO.getEmailId());
        reqDTO.setMobileNo(appDTO.getMobileNo());
        reqDTO.setTitleId(appDTO.getApplicantTitle());
        reqDTO.setBldgName(appDTO.getBuildingName());
        reqDTO.setRoadName(appDTO.getRoadName());
        reqDTO.setAreaName(appDTO.getAreaName());
        if ((appDTO.getPinCode() != null) && !appDTO.getPinCode().isEmpty()) {
            reqDTO.setPincodeNo(Long.valueOf(appDTO.getPinCode()));
        }
        reqDTO.setWing(appDTO.getWing());
        reqDTO.setBplNo(appDTO.getBplNo());
        reqDTO.setDeptId(reqDTO.getDeptId());
        reqDTO.setFloor(appDTO.getFloorNo());
        reqDTO.setWardNo(appDTO.getDwzid2());
        reqDTO.setZoneNo(appDTO.getDwzid1());
        reqDTO.setCityName(appDTO.getVillageTownSub());
        reqDTO.setBlockName(appDTO.getBlockName());
        reqDTO.setHouseComplexName(appDTO.getHousingComplexName());
        reqDTO.setFlatBuildingNo(appDTO.getFlatBuildingNo());
        if ((appDTO.getAadharNo() != null) && !appDTO.getAadharNo().equals("")) {
            reqDTO.setUid(Long.valueOf(appDTO.getAadharNo().replaceAll("\\s", "")));
        }
        final List<LookUp> lookUps = CommonMasterUtility.getLookUps(MainetConstants.GENDER, organisation);
        for (final LookUp lookUp : lookUps) {
            if ((appDTO.getGender() != null) && !appDTO.getGender().isEmpty()) {
                if (lookUp.getLookUpId() == Long.parseLong(appDTO.getGender())) {
                    appDTO.setGender(lookUp.getLookUpCode());
                    break;
                }
            }

        }
        reqDTO.setGender(appDTO.getGender());
    }

    @Override
    @Transactional(readOnly = true)
    public WardZoneBlockDTO getWordZoneBlockByApplicationId(final Long applicationId, final Long serviceId, final Long orgId) {
        final Long id = estateBookingRepository.getApplicantInformationById(applicationId, orgId);

        WardZoneBlockDTO wardZoneDTO = null;

        if (id != null) {
            wardZoneDTO = new WardZoneBlockDTO();
            wardZoneDTO.setAreaDivision1(id);

        }
        return wardZoneDTO;
    }

    @Override
    @Transactional(readOnly = true)
    public List<EstateBookingDTO> findByPropId(final Long propId, final Long orgId) {
        final List<EstateBooking> estateBooking = estateBookingRepository
                .findByEstatePropertyEntityPropIdAndOrgIdAndBookingStatusNotIn(propId, orgId, "U");
        final List<EstateBookingDTO> list = new ArrayList<>();
        EstateBookingDTO bookingDTO = null;
        if (null != estateBooking) {
            for (final EstateBooking booking : estateBooking) {
                bookingDTO = new EstateBookingDTO();
                bookingDTO.setId(booking.getId());
                bookingDTO.setToDate(booking.getToDate());
                bookingDTO.setFromDate(booking.getFromDate());
                bookingDTO.setShiftName(getShiftNameById(booking));
                bookingDTO.setBookingStatus(booking.getBookingStatus());
                list.add(bookingDTO);
            }
        }
        return list;
    }

    private String getShiftNameById(final EstateBooking estateBooking) {
        String name = null;
        final Organisation organisation = new Organisation();
        organisation.setOrgid(estateBooking.getOrgId());
        final List<LookUp> lookUps = CommonMasterUtility.getLookUps(PrefixConstants.SHIFT_PREFIX, organisation);
        for (final LookUp lookUp : lookUps) {
            if (lookUp.getLookUpId() == estateBooking.getShiftId()) {
                name = lookUp.getLookUpCode();
                break;
            }
        }
        return name;
    }

    @Override
    @Transactional(readOnly = true)
    public List<String> getEstateBookingFromAndToDates(final Long propId, final Long orgId) {
        final Organisation organisation = iOrganisationService.getOrganisationById(orgId);
        final LookUp lookup = CommonMasterUtility.getValueFromPrefixLookUp(MainetConstants.SHIFT_PREFIX_GENERAL,
                PrefixConstants.SHIFT_PREFIX, organisation);
        List<String> dateList = null;
        final List<Object[]> objects = estateBookingRepository.getFromAndToDates(propId, lookup.getLookUpId(), orgId);
        dateList = new ArrayList<>();
        for (final Object[] obj : objects) {
            getDaysBetweenDates(dateList, (Date) obj[0], (Date) obj[1]);
        }
        return dateList;
    }

    @Override
    @Transactional(readOnly = true)
    public List<String> getEstateBookingFromAndToDatesForGeneral(final Long propId, final Long orgId) {
        final List<Object[]> objects = estateBookingRepository.getFromAndToDatesForGeneral(propId, orgId);
        final List<String> dateList = new ArrayList<>();
        for (final Object[] obj : objects) {
            getDaysBetweenDates(dateList, (Date) obj[0], (Date) obj[1]);
        }
        return dateList;
    }

    private List<String> getDaysBetweenDates(final List<String> dateList, final Date startdate, final Date enddate) {
        final Calendar calendar = new GregorianCalendar();
        calendar.setTime(startdate);
        while (calendar.getTime().before(enddate) || calendar.getTime().equals(enddate)) {
            final Date result = calendar.getTime();
            dateList.add(new SimpleDateFormat("d-M-yyyy").format(result));
            calendar.add(Calendar.DATE, 1);
        }
        return dateList;
    }

    @Override
    @Transactional(readOnly = true)
    public List<LookUp> getEstateBookingShifts(final Long propId, final String fromdate, final String todate, final Long orgId) {
        List<LookUp> lookupShift = null;
        final Organisation organisation = iOrganisationService.getOrganisationById(orgId);
        if (fromdate.equals(todate)) {
            final List<LookUp> lookup = CommonMasterUtility.getListLookup(PrefixConstants.SHIFT_PREFIX, organisation);
            final Date dateD = UtilityService.convertStringDateToDateFormat(fromdate);
            lookupShift = new ArrayList<>();
            final List<Long> fromAndtoDate = estateBookingRepository.getShitIdFromDate(propId, dateD, orgId);
            if ((fromAndtoDate == null) || fromAndtoDate.isEmpty()) {
                return lookup;
            } else {
                for (final LookUp look : lookup) {
                    if (!fromAndtoDate.contains(look.getLookUpId())) {
                        if (!look.getLookUpCode().equalsIgnoreCase(MainetConstants.SHIFT_PREFIX_GENERAL)) {
                            lookupShift.add(look);
                        }
                    }
                }
            }
        } else {
            final LookUp lookup = CommonMasterUtility.getValueFromPrefixLookUp(MainetConstants.SHIFT_PREFIX_GENERAL,
                    PrefixConstants.SHIFT_PREFIX, organisation);
            lookupShift = new ArrayList<>();
            lookupShift.add(lookup);
        }
        return lookupShift;
    }

    @Override
    @Transactional(readOnly = true)
    public PropInfoDTO findBooking(final Long bookId, final Long orgId) {
        final EstateBooking estateBooking = estateBookingRepository.findByIdAndOrgId(bookId, orgId);
        final TbCfcApplicationMstJpaRepository tbCfcApplicationMstJpaRepository = ApplicationContextProvider
                .getApplicationContext()
                .getBean(TbCfcApplicationMstJpaRepository.class);
        final TbCfcApplicationAddressJpaRepository tbCfcApplicationAddressJpaRepository = ApplicationContextProvider
                .getApplicationContext().getBean(TbCfcApplicationAddressJpaRepository.class);
        final List<Object[]> appliInfoList = tbCfcApplicationMstJpaRepository.getApplicantInfo(estateBooking.getApplicationId(),
                orgId);
        final List<Object[]> addressInfoList = tbCfcApplicationAddressJpaRepository.findAddressInfo(
                estateBooking.getApplicationId(),
                orgId);
        final PropInfoDTO propInfoDTO = new PropInfoDTO();
        propInfoDTO.setToDate(estateBooking.getToDate());
        propInfoDTO.setFromDate(estateBooking.getFromDate());
        propInfoDTO.setDayPeriod(getShiftNameById(estateBooking));
        propInfoDTO.setBookingPuprpose(estateBooking.getPurpose());
        propInfoDTO.setBookingId(estateBooking.getBookingNo().toString());
        propInfoDTO.setPropName(estateBooking.getEstatePropertyEntity().getName());
        String mName = "";
        for (final Object[] strings : appliInfoList) {
            if (strings[1] != null) {
                mName = strings[1].toString();
            }
            propInfoDTO.setApplicantName(strings[0].toString() + " " + mName + " " + strings[2].toString());
        }
        for (final Object[] strings : addressInfoList) {
            propInfoDTO.setAreaName(strings[0].toString());
            propInfoDTO.setCity(strings[1].toString());
            propInfoDTO.setPinCode(strings[2].toString());
            propInfoDTO.setContactNo(strings[3].toString());
        }

        final Organisation organisation = new Organisation();
        organisation.setOrgid(orgId);
        final LookUp lookup = CommonMasterUtility
                .getHierarchicalLookUp(estateBooking.getEstatePropertyEntity().getEstateEntity().getType2(), organisation);
        propInfoDTO.setCategory(lookup.getLookUpDesc());
        return propInfoDTO;
    }

    @Override
    @Transactional
    public boolean saveFreezeProperty(final EstateBookingDTO estateBookingDTO) {
        EstateBooking estateBooking = new EstateBooking();
        BeanUtils.copyProperties(estateBookingDTO, estateBooking);
        estateBooking.setApplicationId(0L);
        estateBooking.setBookingNo("0");
        estateBooking.setBookingDate(new Date());
        final EstatePropertyEntity estatePropertyEntity = new EstatePropertyEntity();
        estatePropertyEntity.setPropId(estateBookingDTO.getPropId());
        estateBooking.setEstatePropertyEntity(estatePropertyEntity);
        estateBooking.setAmount(0.0);
        estateBooking.setSecurityAmount(0.0); // HArd Code
        estateBooking.setTermAccepted(false);
        estateBooking.setPayFlag(false);
        estateBooking.setBookingStatus(MainetConstants.RnLCommon.F_FLAG);
        estateBooking = estateBookingRepository.save(estateBooking);
        return true;
    }

    @Override
    @Transactional(readOnly = true)
    public List<PropFreezeDTO> findAllFreezeBookingProp(final Long orgId, final String bookingStatus) {
        final List<EstateBooking> estateBookingList = estateBookingRepository.findByOrgIdAndBookingStatus(orgId, bookingStatus);
        final Organisation organisation = new Organisation();
        organisation.setOrgid(orgId);
        PropFreezeDTO propFreezeDTO = null;
        final List<PropFreezeDTO> dtos = new ArrayList<>();
        for (final EstateBooking estateBooking : estateBookingList) {
            propFreezeDTO = new PropFreezeDTO();
            propFreezeDTO.setId(estateBooking.getId());
            propFreezeDTO.setEstate(estateBooking.getEstatePropertyEntity().getEstateEntity().getNameEng());
            propFreezeDTO.setProperty(estateBooking.getEstatePropertyEntity().getName());
            propFreezeDTO.setLocation(estateBooking.getEstatePropertyEntity().getEstateEntity().getLocationMas().getLocNameEng());
            propFreezeDTO.setFromDate(UtilityService.convertDateToDDMMYYYY(estateBooking.getFromDate()));
            propFreezeDTO.setToDate(UtilityService.convertDateToDDMMYYYY(estateBooking.getToDate()));
            propFreezeDTO.setShift(
                    CommonMasterUtility.getNonHierarchicalLookUpObject(estateBooking.getShiftId(), organisation).getLookUpDesc());
            propFreezeDTO.setPurpose(estateBooking.getPurpose());
            dtos.add(propFreezeDTO);
        }
        return dtos;
    }

    @Override
    @Transactional
    public int updateFreezeProperty(final Long id, final Long empId) {
        estateBookingRepository.updateFreezeProperty(id, empId);
        return 0;
    }

    @Override
    @Transactional(readOnly = true)
    public boolean findCountForProperty(final Long orgId, final Long propId) {
        boolean count = true;
        if (estateBookingRepository.findCountForProperty(orgId, propId) > 0L) {
            count = false;
        }
        return count;
    }

}
