package com.avitam.bankloanapplication.web.controllers.admin.loanapplication;

import com.avitam.bankloanapplication.model.dto.CustomerDto;
import com.avitam.bankloanapplication.model.dto.LoanApplicationDto;
import com.avitam.bankloanapplication.model.dto.LoanApplicationWsDto;
import com.avitam.bankloanapplication.model.dto.LoanDto;
import com.avitam.bankloanapplication.model.dto.LoanTemplateDto;
import com.avitam.bankloanapplication.model.dto.LoanTypeDto;
import com.avitam.bankloanapplication.model.dto.LoanWsDto;
import com.avitam.bankloanapplication.model.dto.SearchDto;
import com.avitam.bankloanapplication.model.entity.Customer;
import com.avitam.bankloanapplication.model.entity.Loan;
import com.avitam.bankloanapplication.model.entity.LoanApplication;
import com.avitam.bankloanapplication.model.entity.LoanTemplate;
import com.avitam.bankloanapplication.model.entity.LoanType;
import com.avitam.bankloanapplication.repository.CustomerRepository;
import com.avitam.bankloanapplication.repository.LoanApplicationRepository;
import com.avitam.bankloanapplication.repository.LoanDetailsRepository;
import com.avitam.bankloanapplication.repository.LoanRepository;
import com.avitam.bankloanapplication.repository.LoanTemplateRepository;
import com.avitam.bankloanapplication.repository.LoanTypeRepository;
import com.avitam.bankloanapplication.service.LoanApplicationService;
import com.avitam.bankloanapplication.service.LoanDetailsService;
import com.avitam.bankloanapplication.service.LoanService;
import com.avitam.bankloanapplication.service.NotificationService;
import com.avitam.bankloanapplication.service.impl.LoanApplicationServiceImpl;
import com.avitam.bankloanapplication.web.controllers.BaseController;
import jakarta.mail.MessagingException;
import org.apache.commons.collections4.CollectionUtils;
import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;


@Controller
@RequestMapping("/loans/loanApplication")

public class LoanApplicationController extends BaseController {

    public static final String ADMIN_LOANAPPLICATION = "/loans/loanApplication";
    private static final Logger LOG = LoggerFactory.getLogger(LoanApplicationController.class);
    @Autowired
    private ModelMapper modelMapper;
    @Autowired
    private LoanRepository loanRepository;
    @Autowired
    private CustomerRepository customerRepository;
    @Autowired
    private LoanApplicationRepository loanApplicationRepository;
    @Autowired
    private LoanDetailsService loanDetailsService;
    @Autowired
    private LoanApplicationService loanApplicationService;
    @Autowired
    private LoanApplicationServiceImpl loanApplicationServiceImpl;
    @Autowired
    private LoanDetailsRepository loanDetailsRepository;
    @Autowired
    private LoanService loanService;
    @Autowired
    private LoanTemplateRepository loanTemplateRepository;
    @Autowired
    private LoanTypeRepository loanTypeRepository;
    @Autowired
    private NotificationService notificationService;

    @PostMapping
    @ResponseBody
    public LoanApplicationWsDto getAllLoanApplications(@RequestBody LoanApplicationWsDto loanApplicationWsDto) {
        Pageable pageable = getPageable(loanApplicationWsDto.getPage(), loanApplicationWsDto.getSizePerPage(), loanApplicationWsDto.getSortDirection(), loanApplicationWsDto.getSortField());
        LoanApplicationDto loanApplicationDto = CollectionUtils.isNotEmpty(loanApplicationWsDto.getLoanApplicationDtos()) ? loanApplicationWsDto.getLoanApplicationDtos().get(0) : new LoanApplicationDto();
        LoanApplication loanApplication = modelMapper.map(loanApplicationDto, LoanApplication.class);
        Page<LoanApplication> page = isSearchActive(loanApplication) != null ? loanApplicationRepository.findAll(Example.of(loanApplication), pageable) : loanApplicationRepository.findAll(pageable);
        Type listType = new TypeToken<List<LoanApplicationDto>>() {
        }.getType();
        List<LoanApplicationDto> loanApplicationDtos = new ArrayList<>();
        List<LoanApplicationDto> loanApplicationDtoList = modelMapper.map(page.getContent(), listType);
        for (LoanApplicationDto loanApplicationDto1 : loanApplicationDtoList) {
            Loan loan = loanRepository.findByRecordId(loanApplicationDto1.getLoanId());
            if (loan != null) {
                loanApplicationDto1.setLoanDto(modelMapper.map(loan, LoanDto.class));
            }
            Customer customer = customerRepository.findByRecordId(loanApplicationDto1.getCustomerId());
            if (customer != null) {
                CustomerDto customerDto = modelMapper.map(customer, CustomerDto.class);
                loanApplicationDto1.setCustomerDto(customerDto);
            }
            LoanTemplate loanTemplate = loanTemplateRepository.findByRecordId(loanApplicationDto1.getLoanTemplateId());
            if (loanTemplate != null) {
                LoanType loanType = loanTypeRepository.findByRecordId(loanTemplate.getLoanType());
                loanApplicationDto1.setLoanTemplateDto(modelMapper.map(loanTemplate, LoanTemplateDto.class));
                if (loanType != null) {
                    loanApplicationDto1.setLoanTypeDto(modelMapper.map(loanType, LoanTypeDto.class));
                }
            }
            loanApplicationDto1.setImages("");
            loanApplicationDtos.add(loanApplicationDto1);
        }
        loanApplicationWsDto.setLoanApplicationDtos(loanApplicationDtos);
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        loanApplicationWsDto.setTotalPages(page.getTotalPages());
        loanApplicationWsDto.setTotalRecords(page.getTotalElements());
        return loanApplicationWsDto;
    }

    @GetMapping("/get")
    @ResponseBody
    public LoanApplicationWsDto getLoanApplication() {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        Type listType = new TypeToken<List<LoanApplicationDto>>() {
        }.getType();
        loanApplicationWsDto.setLoanApplicationDtos(modelMapper.map(loanApplicationRepository.findByStatusOrderByIdentifier(true), listType));
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }

    @PostMapping("/getLoanTypeForLoanApplication")
    @ResponseBody
    public LoanApplicationWsDto getLoanTypeForLoanApplication() {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        Type listType = new TypeToken<List<LoanApplicationDto>>() {
        }.getType();
        List<LoanApplication> loanApplicationList = loanApplicationRepository.findAll();
        List<LoanApplicationDto> loanApplicationDtoList = new ArrayList<>();
        LoanApplicationDto loanApplicationDto = new LoanApplicationDto();

        for (LoanApplication loanApplication : loanApplicationList) {
            LoanType loanType = loanTypeRepository.findByRecordId(loanApplication.getLoanTypeId());
            modelMapper.map(loanApplication, loanApplicationDto);
            loanApplicationDto.setLoanTypeDto(modelMapper.map(loanType, LoanTypeDto.class));
        }
        loanApplicationDtoList.add(loanApplicationDto);

        loanApplicationWsDto.setLoanApplicationDtos(modelMapper.map(loanApplicationDtoList, listType));
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }

    @PostMapping("/getLoansByStatus")
    @ResponseBody
    public LoanApplicationWsDto getLoansByStatus(@RequestBody LoanApplicationDto loanApplicationDto) {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        List<LoanApplication> filteredLoans = loanApplicationRepository.findByLoanStatus(loanApplicationDto.getLoanStatus());
        Type listType = new TypeToken<List<LoanApplicationDto>>() {
        }.getType();
        List<LoanApplicationDto> loanApplicationDtoList = modelMapper.map(filteredLoans, listType);
        List<LoanApplicationDto> loanApplicationDtos = new ArrayList<>();

        for (LoanApplicationDto loanApplicationDto1 : loanApplicationDtoList) {
            Loan loan = loanRepository.findByRecordId(loanApplicationDto1.getLoanId());
            if (loan != null) {
                loanApplicationDto1.setLoanDto(modelMapper.map(loan, LoanDto.class));
            }
            Customer customer = customerRepository.findByRecordId(loanApplicationDto1.getCustomerId());
            if (customer != null) {
                CustomerDto customerDto = modelMapper.map(customer, CustomerDto.class);
                loanApplicationDto1.setCustomerDto(customerDto);
            }
            LoanTemplate loanTemplate = loanTemplateRepository.findByRecordId(loanApplicationDto1.getLoanTemplateId());
            if (loanTemplate != null) {
                LoanType loanType = loanTypeRepository.findByRecordId(loanTemplate.getLoanType());
                loanApplicationDto1.setLoanTemplateDto(modelMapper.map(loanTemplate, LoanTemplateDto.class));
                if (loanType != null) {
                    loanApplicationDto1.setLoanTypeDto(modelMapper.map(loanType, LoanTypeDto.class));
                }
            }
            loanApplicationDto1.setImages("");
            loanApplicationDtos.add(loanApplicationDto1);
        }

        loanApplicationWsDto.setLoanApplicationDtos(loanApplicationDtos);
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        loanApplicationWsDto.setAppliedCount(loanApplicationRepository.countByLoanStatus("Applied").intValue());
        loanApplicationWsDto.setApprovedCount(loanApplicationRepository.countByLoanStatus("Approved").intValue());
        loanApplicationWsDto.setRejectedCount(loanApplicationRepository.countByLoanStatus("Rejected").intValue());
        return loanApplicationWsDto;
    }

    @PostMapping("/getLoansByStatusAndId")
    @ResponseBody
    public LoanApplicationWsDto getLoansByStatusAndId(@RequestBody LoanApplicationDto loanApplicationDto) {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        List<LoanApplicationDto> loanApplicationDtoList = new ArrayList<>();
        List<LoanApplication> loanApplications = loanApplicationRepository.findByCustomerId(loanApplicationDto.getCustomerId());
        List<LoanApplication> filteredLoans = new ArrayList<>(loanApplications.stream().filter(loanApplication -> loanApplication.getLoanStatus().equalsIgnoreCase("Completed") ||
                loanApplication.getLoanStatus().equalsIgnoreCase("Approved")).toList());

        for (LoanApplication loanApplication : filteredLoans) {
            LoanApplicationDto loanApplicationDto1 = modelMapper.map(loanApplication, LoanApplicationDto.class);
            Loan loan = loanRepository.findByRecordId(loanApplication.getLoanId());
            if (loan != null) {
                loanApplicationDto1.setLoanDto(modelMapper.map(loan, LoanDto.class));
            }
            LoanType loanType = loanTypeRepository.findByRecordId(loanApplication.getLoanTypeId());
            if (loanType != null) {
                loanApplicationDto1.setLoanTypeDto(modelMapper.map(loanType, LoanTypeDto.class));
            }
            loanApplicationDtoList.add(loanApplicationDto1);
        }

        loanApplicationWsDto.setLoanApplicationDtos(loanApplicationDtoList);
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }

    @PostMapping("/getLoanTemplatesByStatusAndId")
    @ResponseBody
    public LoanApplicationWsDto getLoansTemplateByStatusAndId(@RequestBody LoanApplicationDto loanApplicationDto) {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        List<LoanApplication> loanApplicationList = loanApplicationRepository.findByCustomerIdAndLoanStatus(loanApplicationDto.getCustomerId(), loanApplicationDto.getLoanStatus());
        Type listType = new TypeToken<List<LoanApplicationDto>>() {
        }.getType();
        List<LoanApplicationDto> loanApplicationDtoList = modelMapper.map(loanApplicationList, listType);
        List<LoanApplicationDto> loanApplicationDtos = new ArrayList<>();
        for (LoanApplicationDto loanApplicationDto1 : loanApplicationDtoList) {
            loanApplicationDto1.setLoanTemplateDto(modelMapper.map(loanTemplateRepository.findByRecordId(loanApplicationDto1.getLoanTemplateId()), LoanTemplateDto.class));
            LoanType loanType = loanTypeRepository.findByRecordId(loanApplicationDto1.getLoanTypeId());
            if (loanType != null) {
                loanApplicationDto1.setLoanTypeDto(modelMapper.map(loanType, LoanTypeDto.class));
            }
            loanApplicationDtos.add(loanApplicationDto1);
        }
        loanApplicationWsDto.setLoanApplicationDtos(loanApplicationDtos);
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }

    @PostMapping("/getLoanApplicationsByStatus")
    @ResponseBody
    public LoanApplicationWsDto getLoanApplicationsByStatus(@RequestBody LoanApplicationDto loanApplicationDto) {

        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        List<LoanApplication> loanApplicationList = loanApplicationRepository.findByLoanStatus(loanApplicationDto.getLoanStatus());
        Type listType = new TypeToken<List<LoanApplicationDto>>() {
        }.getType();
        List<LoanApplicationDto> loanApplicationDtoList = modelMapper.map(loanApplicationList, listType);
        List<LoanApplicationDto> loanApplicationDtos = new ArrayList<>();
        if ((loanApplicationDto.getLoanStatus() != null)) {
            for (LoanApplicationDto loanApplicationDto1 : loanApplicationDtoList) {
                loanApplicationDto1.setLoanTemplateDto(modelMapper.map(loanTemplateRepository.findByRecordId(loanApplicationDto1.getLoanTemplateId()), LoanTemplateDto.class));
                loanApplicationDtos.add(loanApplicationDto1);
            }
        } else {
            List<LoanApplication> loanApplicationList1 = loanApplicationRepository.findAll();
            for (LoanApplication loanApplication : loanApplicationList1) {
                LoanApplicationDto loanApplicationDto1 = new LoanApplicationDto();
                LoanType loanType = loanTypeRepository.findByRecordId(loanApplication.getLoanTypeId());
                modelMapper.map(loanApplication, loanApplicationDto1);
                loanApplicationDto1.setLoanTypeDto(modelMapper.map(loanType, LoanTypeDto.class));
                loanApplicationDtos.add(loanApplicationDto1);
            }
        }
        loanApplicationWsDto.setLoanApplicationDtos(loanApplicationDtos);
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }


    @PostMapping("/updateLoansStatus")
    @ResponseBody
    public LoanApplicationWsDto updateLoansStatus(@RequestBody LoanApplicationDto loanApplicationDto) throws IOException, MessagingException {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        loanApplicationService.updateLoanStatus(loanApplicationDto);

        loanApplicationWsDto.setMessage("Loan status updated successfully!!");
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }


    @PostMapping("/getedit")
    @ResponseBody
    public LoanApplicationWsDto editLoanApplication(@RequestBody LoanApplicationWsDto request) {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        List<LoanApplication> loanApplications = new ArrayList<>();
        for (LoanApplicationDto loanApplicationDto : request.getLoanApplicationDtos()) {
            loanApplications.add(loanApplicationRepository.findByRecordId(loanApplicationDto.getRecordId()));
        }
        Type listType = new TypeToken<List<LoanApplicationDto>>() {
        }.getType();
        loanApplicationWsDto.setLoanApplicationDtos(modelMapper.map(loanApplications, listType));
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }

    @PostMapping("/edit")
    @ResponseBody
    public LoanApplicationWsDto handleEdit(@RequestBody LoanApplicationWsDto request) {
        return loanApplicationService.handleEdit(request);
    }

    @GetMapping("/getLoan")
    @ResponseBody
    public LoanWsDto getLoanApplicationResult(@RequestBody LoanApplicationWsDto request) {
        return loanApplicationServiceImpl.getLoanApplicationResult(request);
    }

    @GetMapping("/add")
    @ResponseBody
    public LoanApplicationWsDto addLoanApplication() {
        LoanApplicationWsDto loanApplicationWsDto = new LoanApplicationWsDto();
        List<LoanApplicationDto> loanApplicationDtoList = new ArrayList<>();
        loanApplicationDtoList.add(loanApplicationRepository.findByStatusOrderByIdentifier(true));
        loanApplicationWsDto.setLoanApplicationDtos(loanApplicationDtoList);
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }

    @PostMapping("/delete")
    @ResponseBody
    public LoanApplicationWsDto deleteLoanApplication(@RequestBody LoanApplicationWsDto loanApplicationWsDto) {

        for (LoanApplicationDto loanApplicationDto1 : loanApplicationWsDto.getLoanApplicationDtos()) {
            loanApplicationRepository.deleteByRecordId(loanApplicationDto1.getRecordId());
        }
        loanApplicationWsDto.setMessage("Data deleted successfully");
        loanApplicationWsDto.setBaseUrl(ADMIN_LOANAPPLICATION);
        return loanApplicationWsDto;
    }

    @GetMapping("/getAdvancedSearch")
    @ResponseBody
    public List<SearchDto> getSearchAttributes() {
        return getGroupedParentAndChildAttributes(new LoanApplication());
    }
}

