package cn.stylefeng.team.view.modular.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.team.form.api.FormItemServiceApi;
import cn.stylefeng.team.form.api.FormModelServiceApi;
import cn.stylefeng.team.form.api.entity.FormModel;
import cn.stylefeng.team.form.api.pojo.FormDesignVo;
import cn.stylefeng.team.meta.api.MetaFieldServiceApi;
import cn.stylefeng.team.meta.api.MetaModelServiceApi;
import cn.stylefeng.team.meta.api.entity.MetaField;
import cn.stylefeng.team.meta.api.entity.MetaModel;
import cn.stylefeng.team.project.api.SqlProviderApi;
import cn.stylefeng.team.project.api.SystemCodeApi;
import cn.stylefeng.team.project.api.enums.SystemCodeTypeEnum;
import cn.stylefeng.team.project.api.exception.enums.ProjectExceptionEnum;
import cn.stylefeng.team.project.api.pojo.SystemCodeInfo;
import cn.stylefeng.team.view.api.constants.ViewConstants;
import cn.stylefeng.team.view.api.entity.View;
import cn.stylefeng.team.view.api.enums.ViewTypeEnum;
import cn.stylefeng.team.view.api.pojo.ParserDetail;
import cn.stylefeng.team.view.api.pojo.ViewColumnField;
import cn.stylefeng.team.view.api.pojo.ViewConfig;
import cn.stylefeng.team.view.modular.enums.ViewExceptionEnum;
import cn.stylefeng.team.view.modular.factory.ViewConfigFactory;
import cn.stylefeng.team.view.modular.mapper.ViewMapper;
import cn.stylefeng.team.view.modular.parser.ParserContainer;
import cn.stylefeng.team.view.modular.pojo.request.ViewRequest;
import cn.stylefeng.team.view.modular.pojo.response.ViewFieldDTO;
import cn.stylefeng.team.view.modular.pojo.response.ViewFieldResult;
import cn.stylefeng.team.view.modular.service.ViewService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ViewServiceImpl extends ServiceImpl<ViewMapper, View> implements ViewService {

    @Resource
    private MetaFieldServiceApi metaFieldServiceApi;

    @Resource
    private SqlProviderApi sqlProviderApi;

    @Resource
    private MetaModelServiceApi metaModelServiceApi;

    @Resource
    private FormModelServiceApi formModelServiceApi;

    @Resource
    private FormItemServiceApi formItemServiceApi;

    @Resource
    private SystemCodeApi systemCodeApi;

    @Override
    public void add(ViewRequest viewRequest) {

        this.validateRepeat(viewRequest, false);

        this.validateOnlyOneListView(viewRequest, false);

        View view = new View();
        BeanUtil.copyProperties(viewRequest, view);

        this.save(view);
    }

    @Override
    public void del(ViewRequest viewRequest) {
        View view = this.queryView(viewRequest);
        this.removeById(view.getViewId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(ViewRequest viewRequest) {
        this.removeByIds(viewRequest.getBatchDeleteIdList());
    }

    @Override
    public void edit(ViewRequest viewRequest) {

        viewRequest.setSystemCode(null);
        viewRequest.setSystemCodeShow(null);

        this.validateRepeat(viewRequest, true);

        this.validateOnlyOneListView(viewRequest, true);

        View view = this.queryView(viewRequest);
        BeanUtil.copyProperties(viewRequest, view);
        this.updateById(view);
    }

    @Override
    public View detail(ViewRequest viewRequest) {

        LambdaQueryWrapper<View> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(View::getViewId, viewRequest.getViewId());

        queryWrapper.select(View::getViewId, View::getBusinessCategoryId, View::getBusinessId, View::getViewName, View::getViewCode, View::getSystemCodeShow, View::getDescription, View::getViewType,
                View::getFldSort);

        return this.getOne(queryWrapper, false);
    }

    @Override
    public List<View> findList(ViewRequest viewRequest) {
        LambdaQueryWrapper<View> wrapper = this.createWrapper(viewRequest);

        wrapper.select(View::getViewId, View::getViewName, View::getViewCode);

        return this.list(wrapper);
    }

    @Override
    public PageResult<View> findPage(ViewRequest viewRequest) {
        LambdaQueryWrapper<View> wrapper = createWrapper(viewRequest);

        wrapper.select(View::getViewId, View::getViewName, View::getViewCode, View::getSystemCode, BaseEntity::getCreateTime, View::getViewType, View::getFldSort);

        Page<View> pageList = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(pageList);
    }

    @Override
    public View getViewConfig(ViewRequest viewRequest) {

        LambdaQueryWrapper<View> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(View::getViewId, viewRequest.getViewId());

        queryWrapper.select(View::getViewId, View::getViewName, View::getViewConfig, View::getBusinessId);

        View view = this.getOne(queryWrapper, false);

        if (view == null) {
            return null;
        }

        ViewConfig viewConfig = view.getViewConfig();
        if (viewConfig != null) {
            ViewConfigFactory.fillViewConfigIds(viewConfig);
        }

        return view;
    }

    @Override
    public void saveViewConfig(ViewRequest viewRequest) {
        View view = this.getById(viewRequest.getViewId());
        view.setViewConfig(viewRequest.getViewConfig());
        this.updateById(view);
    }

    @Override
    public ViewFieldDTO getImportFieldList(ViewRequest viewRequest) {

        ViewFieldDTO viewFieldDTO = new ViewFieldDTO();

        LambdaQueryWrapper<View> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(View::getViewId, viewRequest.getViewId());
        queryWrapper.select(View::getBusinessId);
        View one = this.getOne(queryWrapper, false);

        Long businessId = one.getBusinessId();

        List<MetaField> businessMainModelFields = metaFieldServiceApi.getBusinessMainModelFields(businessId);
        if (ObjectUtil.isEmpty(businessMainModelFields)) {
            return viewFieldDTO;
        }

        List<ViewColumnField> metaFieldList = new ArrayList<>();
        for (MetaField businessMainModelField : businessMainModelFields) {
            ViewColumnField viewColumnField = new ViewColumnField();
            viewColumnField.setFieldName(businessMainModelField.getFieldCode());
            viewColumnField.setShowName(businessMainModelField.getDescription());
            viewColumnField.setColumnShowFlag(true);
            metaFieldList.add(viewColumnField);
        }
        viewFieldDTO.setMetaFieldList(metaFieldList);

        List<String> fieldList = metaFieldList.stream().map(ViewColumnField::getFieldName).collect(Collectors.toList());

        MetaModel mainMetaModel = metaModelServiceApi.getMainMetaModel(businessId);
        String metaModelCode = mainMetaModel.getMetaModelCode();

        String defaultSelectSQL = sqlProviderApi.selectSql(fieldList, metaModelCode);
        viewFieldDTO.setDefaultSql(defaultSelectSQL);

        return viewFieldDTO;
    }

    @Override
    public List<ViewColumnField> getViewFieldList(ViewRequest viewRequest) {
        View view = this.getById(viewRequest.getViewId());
        if (view == null) {
            return new ArrayList<>();
        }

        ViewConfig viewConfig = view.getViewConfig();
        if (viewConfig == null) {
            return new ArrayList<>();
        }

        return viewConfig.getViewColumnFieldList();
    }

    @Override
    public List<FormModel> getViewBusinessFormList(ViewRequest viewRequest) {
        View view = this.getById(viewRequest.getViewId());
        if (view == null) {
            return new ArrayList<>();
        }

        Long businessId = view.getBusinessId();

        return formModelServiceApi.getBusinessFormModelList(businessId);
    }

    @Override
    public ViewFieldResult getFormBasedFieldList(ViewRequest viewRequest) {

        Long businessId = viewRequest.getBusinessId();
        List<MetaField> businessMainModelFields = metaFieldServiceApi.getBusinessMainModelFields(businessId);

        List<FormDesignVo> businessFromDesignList = formItemServiceApi.getBusinessFromDesignList(businessId);

        return ViewConfigFactory.createFormBasedFieldList(businessMainModelFields, businessFromDesignList);
    }

    @Override
    public ViewConfig getViewConfig(Long viewId, boolean filterNotShow) {
        LambdaQueryWrapper<View> viewLambdaQueryWrapper = new LambdaQueryWrapper<>();

        viewLambdaQueryWrapper.eq(View::getViewId, viewId);

        return processViewConfig(viewLambdaQueryWrapper, filterNotShow);
    }

    @Override
    public ViewConfig getViewConfigByBusinessId(Long businessId, boolean filterNotShow) {

        LambdaQueryWrapper<View> viewLambdaQueryWrapper = new LambdaQueryWrapper<>();

        viewLambdaQueryWrapper.eq(View::getBusinessId, businessId);
        viewLambdaQueryWrapper.eq(View::getViewType, ViewTypeEnum.LIST_VIEW.getCode());

        return processViewConfig(viewLambdaQueryWrapper, filterNotShow);
    }

    @Override
    public Object getFormattedValue(String formatterName, Object originValue) {

        if (StrUtil.isBlank(formatterName)) {
            return originValue;
        }

        if (ObjectUtil.isEmpty(originValue)) {
            return originValue;
        }

        try {
            ParserDetail parser = ParserContainer.getParser(formatterName);
            return parser.getFormatProcess().formatProcess(originValue);
        } catch (Exception e) {
            log.error("格式化数据出错！", e);
            return originValue;
        }
    }

    @Override
    public List<View> getBusinessViewList(Long businessId) {
        LambdaQueryWrapper<View> viewLambdaQueryWrapper = new LambdaQueryWrapper<>();
        viewLambdaQueryWrapper.select(View::getViewId, View::getViewName);
        viewLambdaQueryWrapper.eq(View::getBusinessId, businessId);
        return this.list(viewLambdaQueryWrapper);
    }

    @Override
    public View getViewByViewId(Long viewId) {
        return this.getById(viewId);
    }

    @Override
    public List<View> getViewListByBusinessId(Long businessId) {
        LambdaQueryWrapper<View> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(View::getBusinessId, businessId);
        queryWrapper.select(View::getViewId, View::getViewName, View::getViewCode);
        return this.list(queryWrapper);
    }

    @Override
    public Map<Long, Integer> statViewNumber(Set<Long> businessIds) {
        HashMap<Long, Integer> resultMap = new HashMap<>();

        if (ObjectUtil.isEmpty(businessIds)) {
            return resultMap;
        }

        LambdaQueryWrapper<View> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(View::getBusinessId, businessIds);
        wrapper.select(View::getBusinessId, View::getViewId);
        List<View> metaDictList = this.list(wrapper);
        if (ObjectUtil.isEmpty(metaDictList)) {
            return resultMap;
        }

        return metaDictList.stream().collect(Collectors.groupingBy(View::getBusinessId, Collectors.summingInt(i -> 1)));
    }

    @Override
    public void createListView(Long businessId, Long businessCategoryId, ViewConfig viewConfig) {
        View view = new View();
        view.setBusinessId(businessId);
        view.setBusinessCategoryId(businessCategoryId);
        view.setViewName(ViewConstants.DEFAULT_VIEW_NAME);
        view.setViewCode(ViewConstants.DEFAULT_VIEW_CODE);
        view.setFldSort(new BigDecimal(100));

        SystemCodeInfo newSystemCode = systemCodeApi.createNewSystemCode(SystemCodeTypeEnum.VIEW);
        view.setSystemCode(newSystemCode.getSystemCode());
        view.setSystemCodeShow(newSystemCode.getSystemCodeShow());

        view.setViewType(ViewTypeEnum.LIST_VIEW.getCode());

        view.setViewConfig(viewConfig);

        this.save(view);
    }

    private View queryView(ViewRequest viewRequest) {
        View view = this.getById(viewRequest.getViewId());
        if (ObjectUtil.isEmpty(view)) {
            throw new ServiceException(ViewExceptionEnum.VIEW_NOT_EXISTED);
        }
        return view;
    }

    private LambdaQueryWrapper<View> createWrapper(ViewRequest viewRequest) {
        LambdaQueryWrapper<View> queryWrapper = new LambdaQueryWrapper<>();

        String searchText = viewRequest.getSearchText();
        if (ObjectUtil.isNotEmpty(searchText)) {
            queryWrapper.nested(i -> {
                i.like(View::getViewName, searchText);
                i.or().like(View::getViewCode, searchText);
                i.or().like(View::getSystemCodeShow, searchText);
            });
        }

        Long businessId = viewRequest.getBusinessId();
        if (ObjectUtil.isNotEmpty(businessId)) {
            queryWrapper.eq(View::getBusinessId, businessId);
        }

        queryWrapper.orderByAsc(View::getFldSort);

        return queryWrapper;
    }

    private void validateRepeat(ViewRequest viewRequest, boolean editFlag) {

        LambdaQueryWrapper<View> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(View::getViewName, viewRequest.getViewName());
        if (editFlag) {
            wrapper1.ne(View::getViewId, viewRequest.getViewId());
        }
        long nameNumber = this.count(wrapper1);
        if (nameNumber > 0) {
            throw new ServiceException(ViewExceptionEnum.VIEW_NAME_REPEAT);
        }

        LambdaQueryWrapper<View> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(View::getViewCode, viewRequest.getViewCode());
        if (editFlag) {
            wrapper2.ne(View::getViewId, viewRequest.getViewId());
        }
        long codeNumber = this.count(wrapper2);
        if (codeNumber > 0) {
            throw new ServiceException(ViewExceptionEnum.VIEW_CODE_REPEAT);
        }

        LambdaQueryWrapper<View> wrapper3 = new LambdaQueryWrapper<>();
        wrapper3.eq(View::getSystemCode, viewRequest.getSystemCode());
        if (editFlag) {
            wrapper3.ne(View::getViewId, viewRequest.getViewId());
        }
        long systemCodeNumber = this.count(wrapper3);

        if (systemCodeNumber > 0) {
            throw new ServiceException(ProjectExceptionEnum.DEVOPS_SYSTEM_CODE_ERROR);
        }
    }

    private ViewConfig processViewConfig(LambdaQueryWrapper<View> viewLambdaQueryWrapper, boolean filterNotShowFlag) {

        viewLambdaQueryWrapper.select(View::getViewConfig);

        View one = this.getOne(viewLambdaQueryWrapper, false);

        if (one == null) {
            return new ViewConfig();
        }

        ViewConfig viewConfig = one.getViewConfig();
        if (filterNotShowFlag) {
            viewConfig.getViewColumnFieldList().removeIf(field -> field.getColumnShowFlag() == null || !field.getColumnShowFlag());
            viewConfig.getViewSearchFieldList().removeIf(field -> field.getSearchShowFlag() == null || !field.getSearchShowFlag());
        }
        return viewConfig;
    }

    private void validateOnlyOneListView(ViewRequest viewRequest, boolean editFlag) {
        if (!ViewTypeEnum.LIST_VIEW.getCode().equals(viewRequest.getViewType())) {
            return;
        }

        LambdaQueryWrapper<View> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(View::getViewType, ViewTypeEnum.LIST_VIEW.getCode());
        wrapper.eq(View::getBusinessId, viewRequest.getBusinessId());
        if (editFlag) {
            wrapper.ne(View::getViewId, viewRequest.getViewId());
        }

        long codeNumber = this.count(wrapper);
        if (codeNumber > 0) {
            throw new ServiceException(ViewExceptionEnum.ONLY_ONE_LIST_VIEW);
        }
    }

}
