package cn.lihongjie.coal.employee.service;

import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.common.Base64File;
import cn.lihongjie.coal.common.ExcelUtils;
import cn.lihongjie.coal.department.dto.CreateDepartmentDto;
import cn.lihongjie.coal.department.dto.DepartmentDto;
import cn.lihongjie.coal.department.entity.DepartmentEntity;
import cn.lihongjie.coal.department.service.DepartmentService;
import cn.lihongjie.coal.dictionary.service.DictionaryService;
import cn.lihongjie.coal.employee.dto.CreateEmployeeDto;
import cn.lihongjie.coal.employee.dto.EmployeeDto;
import cn.lihongjie.coal.employee.dto.ImportEmpFromExcelRequest;
import cn.lihongjie.coal.employee.dto.UpdateEmployeeDto;
import cn.lihongjie.coal.employee.entity.EmployeeEntity;
import cn.lihongjie.coal.employee.mapper.EmployeeMapper;
import cn.lihongjie.coal.employee.repository.EmployeeRepository;
import cn.lihongjie.coal.file.service.FileService;
import cn.lihongjie.coal.jobPost.dto.CreateJobPostDto;
import cn.lihongjie.coal.jobPost.entity.JobPostEntity;
import cn.lihongjie.coal.jobPost.service.JobPostService;

import io.vavr.Function1;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.List;

@Service
@Slf4j
@Transactional
public class EmployeeService extends BaseService<EmployeeEntity, EmployeeRepository> {
    public static final List<String> HEADERS =
            List.of(
                    "姓名", "性别", "民族", "婚姻状况", "入职时间", "身份证号", "学历", "毕业学校", "籍贯", "住址", "手机号", "部门",
                    "岗位", "银行卡号", "收款人姓名");
    @Autowired private EmployeeRepository repository;

    @Autowired private EmployeeMapper mapper;

    @Autowired private ConversionService conversionService;

    public EmployeeDto create(CreateEmployeeDto request) {
        EmployeeEntity entity = mapper.toEntity(request);

        this.repository.save(entity);
        return getById(entity.getId());
    }

    public EmployeeDto update(UpdateEmployeeDto request) {
        EmployeeEntity entity = this.repository.get(request.getId());
        this.mapper.updateEntity(entity, request);

        this.repository.save(entity);

        return getById(entity.getId());
    }

    public void delete(IdRequest request) {
        this.repository.deleteAllById(request.getIds());
    }

    @Autowired FileService fileService;

    public EmployeeDto getById(String id) {
        EmployeeEntity entity = repository.get(id);

        return toDto(entity);
    }

    private EmployeeDto toDto(EmployeeEntity entity) {
        EmployeeDto dto = mapper.toDto(entity);
        if (CollectionUtils.isNotEmpty(dto.getCerts())) {
            dto.getCerts()
                    .forEach(
                            x -> {
                                if (CollectionUtils.isNotEmpty(x.getFileIds()))
                                    x.setFiles(fileService.findAllByIds(x.getFileIds()));
                            });
        }
        if (CollectionUtils.isNotEmpty(dto.getCars())) {
            dto.getCars()
                    .forEach(
                            x -> {
                                if (CollectionUtils.isNotEmpty(x.getFileIds()))
                                    x.setFiles(fileService.findAllByIds(x.getFileIds()));
                            });
        }
        return dto;
    }

    public Page<EmployeeDto> list(CommonQuery query) {
        Page<EmployeeEntity> page =
                repository.findAll(
                        query.specification(conversionService),
                        PageRequest.of(
                                query.getPageNo(),
                                query.getPageSize(),
                                Sort.by(query.getOrders())));

        return page.map(this::toDto);
    }

    @Autowired DictionaryService dictionaryService;
    @Autowired DepartmentService departmentService;
    @Autowired JobPostService jobPostService;
    @PersistenceContext private EntityManager entityManager;

    public Base64File generateImportTemplate() {

        Base64File base64File = new Base64File();
        ExcelUtils.genSingleSheetToBase64File(
                "员工导入模板.xlsx",
                HEADERS,
                null,
                base64File,
                ExcelUtils.SheetCustomizer.headerBasedConstrainCustomizer(
                        name -> {
                            return switch (name) {
                                case "入职时间" ->
                                        new ExcelUtils.ValidationInfo.DateValidationInfo();
                                case "性别" ->
                                        new ExcelUtils.ValidationInfo.ListValidationInfo(
                                                dictionaryService.getExcelValidation("sex"));
                                case "民族" ->
                                        new ExcelUtils.ValidationInfo.ListValidationInfo(
                                                dictionaryService.getExcelValidation("nation"));
                                case "婚姻状况" ->
                                        new ExcelUtils.ValidationInfo.ListValidationInfo(
                                                dictionaryService.getExcelValidation("marriage"));
                                case "学历" ->
                                        new ExcelUtils.ValidationInfo.ListValidationInfo(
                                                dictionaryService.getExcelValidation("education"));
                                default -> new ExcelUtils.ValidationInfo.AnyValidationInfo();
                            };
                        }).andThen(ExcelUtils.SheetCustomizer.autoSizeColumnCustomizer()).andThen(ExcelUtils.SheetCustomizer.centerFirstRowCustomizer()));
        return base64File;
    }

    @SneakyThrows
    public void importFromExcel(ImportEmpFromExcelRequest request) {

        @Cleanup InputStream download = fileService.download(request.getFileId());

        io.vavr.Function1<String, String> getDepartmentId =
                Function1.of(
                                (String name) -> {
                                    DepartmentDto department = departmentService.findByName(name);

                                    if (department == null) {
                                        CreateDepartmentDto dto = new CreateDepartmentDto();
                                        dto.setName(name);
                                        department = departmentService.create(dto);
                                    }

                                    return department == null ? null : department.getId();
                                })
                        .memoized();

        io.vavr.Function1<String, String> getJobPostId =
                Function1.of(
                                (String name) -> {
                                    var jobPost = jobPostService.findByName(name);
                                    if (jobPost == null) {
                                        CreateJobPostDto dto = new CreateJobPostDto();
                                        dto.setName(name);
                                        jobPost = jobPostService.create(dto);
                                    }
                                    return jobPost == null ? null : jobPost.getId();
                                })
                        .memoized();

        List<EmployeeEntity> list =
                ExcelUtils.readFirstSheetToObjectList(
                        download,
                        EmployeeEntity::new,
                        (header, emp, value) -> {
                            switch (header) {
                                case "姓名" -> emp.setName((String) value);
                                case "性别" -> emp.setSex(ExcelUtils.getCode(value));
                                case "民族" -> emp.setNation(ExcelUtils.getCode(value));
                                case "婚姻状况" -> emp.setMarriage(ExcelUtils.getCode(value));
                                case "入职时间" -> emp.setEntryDate(ExcelUtils.getLocalDate(value));
                                case "身份证号" -> emp.setIdCard((String) value);
                                case "学历" -> emp.setEducation(ExcelUtils.getCode(value));
                                case "毕业学校" -> emp.setSchool((String) value);
                                case "籍贯" -> emp.setNativePlace((String) value);
                                case "住址" -> emp.setAddress((String) value);
                                case "手机号" -> emp.setPhone((String) value);
                                case "部门" ->
                                        emp.setDepartment(
                                                entityManager.getReference(
                                                        DepartmentEntity.class,
                                                        getDepartmentId.apply((String) value)));
                                case "岗位" ->
                                        emp.setJobPost(
                                                entityManager.getReference(
                                                        JobPostEntity.class,
                                                        getJobPostId.apply((String) value)));
                                case "银行卡号" -> emp.setBank(ExcelUtils.getCode(value));
                                case "收款人姓名" -> emp.setBankName((String) value);
                            }
                        });

        list.forEach(x -> x.setEmpStatus("1"));

        this.repository.saveAll(list);
    }
}
