package com.yihu.hos.standard.service.adapter;

import com.yihu.hos.core.datatype.CollectionUtil;
import com.yihu.hos.core.datatype.StringUtil;
import com.yihu.hos.standard.model.adapter.AdapterSchemeModel;
import com.yihu.hos.standard.model.adapter.AdapterSchemeVersionModel;
import com.yihu.hos.standard.model.adapter.resultModel.AdapterSchemeVersionResultDetailModel;
import com.yihu.hos.standard.model.standard.StandardModel;
import com.yihu.hos.standard.model.standard.StdMetaDataModel;
import com.yihu.hos.standard.service.bo.AdapterVersion;
import com.yihu.hos.standard.service.bo.StandardVersion;
import com.yihu.hos.web.framework.constant.ErrorCode;
import com.yihu.hos.web.framework.constant.SqlConstants;
import com.yihu.hos.web.framework.dao.SQLGeneralDAO;
import com.yihu.hos.web.framework.exception.ApiException;
import com.yihu.hos.web.framework.model.*;
import com.yihu.hos.web.framework.util.sql.SqlCreator;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @Created by lingfeng 2015/12/25.
 */
@Transactional
@Service("AdapterSchemeVersionService")
public class AdapterSchemeVersionService extends SQLGeneralDAO {

    public static final String BEAN_ID = "AdapterSchemeVersionService";

    public DictionaryResult getDictionaryResult(Integer schemeId, String condition, String order, Integer rows, Integer page) {
        try {
            List<AdapterSchemeVersionModel> adapterSchemeVersionModelList = getEntityListByParentId(
                    AdapterSchemeVersionModel.class, "schemeId", schemeId, condition, order, rows, page);
            DictionaryResult dictionaryResult = DictionaryResult.success("获取适配方案版本列表成功");
            List<DictItem> detailModelList = new ArrayList<DictItem>();
            DictItem item = new DictItem();
            item.setCode("");
            item.setValue("-无-");
            detailModelList.add(item);
            for (AdapterSchemeVersionModel adapterSchemeVersionModel : adapterSchemeVersionModelList) {
                DictItem items = new DictItem();
                String id = adapterSchemeVersionModel.getVersion();
                String name = adapterSchemeVersionModel.getName();
                items.setCode(id.toString());
                items.setValue(name);
                detailModelList.add(items);
            }
            dictionaryResult.setSuccessFlg(true);
            dictionaryResult.setMessage("获取适配方案版本成功");
            dictionaryResult.setDetailModelList(detailModelList);
            return dictionaryResult;
        } catch (Exception e) {
            return DictionaryResult.error("获取适配方案版本失败");
        }
    }

    public Result add(String version) {
        Boolean existFlg = false;
        try {
            AdapterSchemeVersionModel versionModel = objectMapper.readValue(version, AdapterSchemeVersionModel.class);
            existFlg = schemeVersionNameValidate(versionModel.getSchemeId(), versionModel.getName());
            if (!existFlg) {
                AdapterVersion adapterVersion = AdapterVersion.generateVersion();
                versionModel.setVersion(adapterVersion.getVersion());
                versionModel.setPublishStatus(0);
                saveEntity(versionModel);
                if (!StringUtil.isEmpty(versionModel.getBaseVersion())) {
                    AdapterSchemeVersionModel baseVersionModel = getByVersion(versionModel.getBaseVersion());
                    AdapterVersion baseVersion = new AdapterVersion(baseVersionModel.getVersion());
                    AdapterVersion newVersion = new AdapterVersion(versionModel.getVersion());
                    buildVersionTables(baseVersion, newVersion);
                } else {
                    buildVersionTables(versionModel);
                }
                versionModel.setSuccessFlg(true);
                versionModel.setMessage("保存适配版本成功");
                return versionModel;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (existFlg) {
            return Result.error("已存在适配方案版本名称");
        } else {
            return Result.error("保存适配方案失败");
        }
    }

    public Boolean schemeVersionNameValidate(Integer schemeId, String name) {
        String sql = "FROM AdapterSchemeVersionModel WHERE schemeId = :schemeId and name = :name";
        Query query = getCurrentSession().createQuery(sql);
        query.setParameter("schemeId", schemeId);
        query.setParameter("name", name);
        List<AdapterSchemeVersionModel> list = query.list();
        if (!CollectionUtil.isEmpty(list)) {
            return true;
        }
        return false;
    }

    public Result publish(String version) {
        return update(version);
    }

    public Result update(String version) {
        Boolean existFlg = false;
        try {
            AdapterSchemeVersionModel versionModel = objectMapper.readValue(version, AdapterSchemeVersionModel.class);
            existFlg = schemeVersionNameValidate(versionModel.getSchemeId(), versionModel.getId(), versionModel.getName());
            if (!existFlg) {
                updateEntity(versionModel);
                versionModel.setSuccessFlg(true);
                versionModel.setMessage("修改适配方案成功");
                return versionModel;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (existFlg) {
            return Result.error("已存在适配方案版本名称");
        } else {
            return Result.error("修改适配方案版本失败");
        }
    }

    public Boolean schemeVersionNameValidate(Integer schemeId, Integer versionId, String name) {
        String sql = "FROM AdapterSchemeVersionModel WHERE schemeId = :schemeId and name = :name and id != :versionId";
        Query query = getCurrentSession().createQuery(sql);
        query.setParameter("schemeId", schemeId);
        query.setParameter("name", name);
        query.setParameter("versionId", versionId);
        List<AdapterSchemeVersionModel> list = query.list();
        if (!CollectionUtil.isEmpty(list)) {
            return true;
        }
        return false;
    }

    public Result delete(Integer versionId) {
        Boolean publishedFlg = false;
        try {
            AdapterSchemeVersionModel versionModel = getEntity(AdapterSchemeVersionModel.class, versionId);
            if (versionModel.getPublishStatus() != null && versionModel.getPublishStatus() == 1) {
                publishedFlg = true;
            } else {
                this.deleteEntity(versionModel);
                deleteVersionTables(new AdapterVersion(versionModel.getVersion()));
                return Result.success("删除适配版本成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (publishedFlg) {
            return Result.success("该适配版本已发布");
        } else {
            return Result.success("删除适配版本失败");
        }
    }

    public Result get(Integer versionId) {
        try {
            AdapterSchemeVersionModel versionModel = getEntity(AdapterSchemeVersionModel.class, versionId);
            versionModel.setSuccessFlg(true);
            versionModel.setMessage("获取适配版本方成功");
            return versionModel;
        } catch (Exception e) {
            return Result.error("获取适配方案失败");
        }
    }

    public AdapterSchemeVersionModel getByVersion(String version) {
        try {
            String sql = "FROM AdapterSchemeVersionModel WHERE version = :version";
            Query query = getCurrentSession().createQuery(sql);
            query.setParameter("version", version);
            List<AdapterSchemeVersionModel> list = query.list();
            if (CollectionUtil.isEmpty(list)) {
                return null;
            }
            AdapterSchemeVersionModel versionModel = list.get(0);
            return versionModel;
        } catch (Exception e) {
            throw new ApiException(ErrorCode.GetSchemeVersionFailed);
        }
    }

    public AdapterSchemeVersionModel fork(Integer versionId) {
        try {
            AdapterSchemeVersionModel baseVersionModel = getEntity(AdapterSchemeVersionModel.class, versionId);
            AdapterSchemeVersionModel newVersionModel = new AdapterSchemeVersionModel();
            newVersionModel.setBaseVersion(baseVersionModel.getVersion());
            newVersionModel.setSchemeId(baseVersionModel.getSchemeId());
            newVersionModel.setPublishTime(new Date());
            this.saveEntity(newVersionModel);
            AdapterVersion baseVersion = new AdapterVersion(baseVersionModel.getVersion());
            AdapterVersion newVersion = new AdapterVersion(newVersionModel.getVersion());
            buildVersionTables(baseVersion, newVersion);
            return newVersionModel;
        } catch (Exception e) {
            throw new ApiException(ErrorCode.SaveSchemeVersionFailed);
        }
    }

    public Result publish(Integer versionId, String publisher) {
        try {
            AdapterSchemeVersionModel adapterSchemeVersionModel = getEntity(AdapterSchemeVersionModel.class, versionId);
            //TODO:generate standard version file;
            adapterSchemeVersionModel.setPublishTime(new Date());
            adapterSchemeVersionModel.setPublishUser(publisher);
            adapterSchemeVersionModel.setPublishStatus(1);
            this.updateEntity(adapterSchemeVersionModel);
            adapterSchemeVersionModel.setSuccessFlg(true);
            adapterSchemeVersionModel.setMessage("发布适配版本成功");
            return adapterSchemeVersionModel;
        } catch (Exception e) {
            return Result.error("发布适配版本失败");
        }
    }

    public List<AdapterSchemeVersionResultDetailModel> getPublishedList() {
        try {
            Session session = getCurrentSession();
            Criteria criteria = session.createCriteria(AdapterSchemeVersionModel.class);
            criteria.add(Restrictions.eq("publishStatus", 1));
            List<AdapterSchemeVersionModel> versionModelList = criteria.list();
            List<Integer> schemeIdList = new ArrayList<>();
            Map<Integer, String> schemeMap = new HashMap<>();
            for (AdapterSchemeVersionModel model : versionModelList) {
                schemeIdList.add(model.getSchemeId());
            }
            if (CollectionUtil.isEmpty(schemeIdList)) {
                schemeIdList.add(null);
            }
            criteria = session.createCriteria(AdapterSchemeModel.class);
            criteria.add(Restrictions.in("id", schemeIdList));
            List<AdapterSchemeModel> schemeList = criteria.list();
            for (AdapterSchemeModel schemeModel : schemeList) {
                schemeMap.put(schemeModel.getId(), schemeModel.getName());
            }

            List<AdapterSchemeVersionResultDetailModel> detailModelList = new ArrayList<>();
            for (AdapterSchemeVersionModel model : versionModelList) {
                AdapterSchemeVersionResultDetailModel detailModel = new AdapterSchemeVersionResultDetailModel();
                detailModel.setId(model.getId());
                detailModel.setName(model.getName());
                detailModel.setSchemeId(model.getSchemeId());
                detailModel.setSchemeName(schemeMap.get(model.getSchemeId()));
                detailModel.setVersion(model.getVersion());
                detailModel.setBaseVersion(model.getBaseVersion());
                detailModel.setPublishStatus(model.getPublishStatus());
                detailModel.setPublishUser(model.getPublishUser());
                detailModel.setPublishTime(model.getPublishTime());
                detailModelList.add(detailModel);
            }

            return detailModelList;
        } catch (Exception e) {
            throw new ApiException(ErrorCode.GetSchemeVersionListFailed);
        }
    }

    public AdapterSchemeVersionModel getEhrAdapterVersionLasted() {
        try {
            List<AdapterSchemeModel> adapterSchemeModelList = getEhrAdapterSchemeList();
            if (CollectionUtil.isEmpty(adapterSchemeModelList)) {
                return null;
            }
            String schemeIdListStr = SqlConstants.EMPTY;
            for (AdapterSchemeModel adapterSchemeModel : adapterSchemeModelList) {
                schemeIdListStr = schemeIdListStr + adapterSchemeModel.getId() + SqlConstants.COMMA;
            }
            schemeIdListStr = SqlConstants.LEFT_BRACKET + schemeIdListStr.substring(0, schemeIdListStr.length() - 1) + SqlConstants.RIGHT_BRACKET;
            String adapterSchemeVersionHql = "SELECT ID AS id,VERSION AS version,SCHEME_ID AS schemeId,NAME AS name FROM ADAPTER_SCHEME_VERSION WHERE PUBLISH_TIME=(select max(PUBLISH_TIME) from ADAPTER_SCHEME_VERSION WHERE SCHEME_ID in " + schemeIdListStr + ")";
            Query adapterSchemeVersionQuery = getCurrentSession().createSQLQuery(adapterSchemeVersionHql);
            Object[] object = (Object[]) adapterSchemeVersionQuery.uniqueResult();
            if (object != null) {
                AdapterSchemeVersionModel adapterSchemeVersionModel = new AdapterSchemeVersionModel();
                adapterSchemeVersionModel.setId((Integer) object[0]);
                adapterSchemeVersionModel.setVersion((String) object[1]);
                adapterSchemeVersionModel.setSchemeId((Integer) object[2]);
                adapterSchemeVersionModel.setName((String) object[3]);
                return adapterSchemeVersionModel;
            } else {
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }

    public List<AdapterSchemeModel> getEhrAdapterSchemeList() {
        String paramSql = "select s.param_value from system_param s where s.param_key = '" + SqlConstants.STANDARD_EHR + "'";
        Query paramQuery = getCurrentSession().createSQLQuery(paramSql);
        String code = (String) paramQuery.uniqueResult();
        if (StringUtil.isEmpty(code)) {
            return null;
        }
        String standardHql = "FROM StandardModel WHERE code = :code";
        Query standardQuery = getCurrentSession().createQuery(standardHql);
        standardQuery.setParameter("code", code);
        StandardModel standardModel = (StandardModel) standardQuery.uniqueResult();
        if (standardModel == null) {
            return null;
        }
        String adapterSchemeHql = "FROM AdapterSchemeModel WHERE adapterStdId = :adapterStdId";
        Query adapterSchemeQuery = getCurrentSession().createQuery(adapterSchemeHql);
        adapterSchemeQuery.setParameter("adapterStdId", standardModel.getId());
        List<AdapterSchemeModel> adapterSchemeModelList = adapterSchemeQuery.list();

        return adapterSchemeModelList;
    }

    public DataGridResult getVersion() throws Exception {
        String SchemeSql = "from AdapterSchemeModel";

        List<AdapterSchemeModel> std = (List<AdapterSchemeModel>) super.hibernateTemplate.find(SchemeSql);
        DataGridResult data = new DataGridResult();
        List<TreeResult> results = new ArrayList<>();
        for (int i = 0; i < std.size(); i++) {
            TreeResult re = new TreeResult();
            //re.setPid("0");
            re.setText(std.get(i).getName());
            re.setId(String.valueOf(std.get(i).getId()));
            results.add(re);
            String sql = "from AdapterSchemeVersionModel ";
            sql += "where schemeId=" + std.get(i).getId();
            List<AdapterSchemeVersionModel> stdModel = (List<AdapterSchemeVersionModel>) super.hibernateTemplate.find(sql);
            for (int j = 0; j < stdModel.size(); j++) {
                TreeResult tmpTree = new TreeResult();
                tmpTree.setId(String.valueOf(stdModel.get(j).getId()));
                tmpTree.setText(stdModel.get(j).getName());
                tmpTree.setPid(String.valueOf(std.get(i).getId()));
                results.add(tmpTree);
            }
        }
        data.setTotalCount(results.size());
        data.setDetailModelList(results);
        return data;

    }

    /**
     * 树型数据
     *
     * @return
     * @throws Exception
     */
    public List<TreeResult> getVersionTree() throws Exception {
        String SchemeSql = "from AdapterSchemeModel";

        List<AdapterSchemeModel> std = (List<AdapterSchemeModel>) super.hibernateTemplate.find(SchemeSql);

        List<TreeResult> results = new ArrayList<>();
        for (int i = 0; i < std.size(); i++) {
            TreeResult re = new TreeResult();
            //re.setPid("0");
            re.setText(std.get(i).getName());
            re.setId(String.valueOf(std.get(i).getId()));
            results.add(re);
            String sql = "from AdapterSchemeVersionModel ";
            sql += "where schemeId=" + std.get(i).getId();
            List<AdapterSchemeVersionModel> stdModel = (List<AdapterSchemeVersionModel>) super.hibernateTemplate.find(sql);
            for (int j = 0; j < stdModel.size(); j++) {
                TreeResult tmpTree = new TreeResult();
                tmpTree.setId(String.valueOf(stdModel.get(j).getId()));
                tmpTree.setText(stdModel.get(j).getName());
                tmpTree.setPid(String.valueOf(std.get(i).getId()));
                results.add(tmpTree);
            }
        }

        return results;

    }

    private void deleteVersionTables(AdapterVersion version) {

        String[] tables = {version.getDataSetTableName(),
                version.getMetaDataTableName(),
                version.getDictTableName(),
                version.getDictEntryTableName(),
                version.getDatasetRelationTableName()};
        List<String> deleteSqlList = new ArrayList<>();
        boolean temp = false;
        for (int i = 0; i < tables.length; ++i) {
            String table = tables[i];
            temp = tableIsExist("SELECT COUNT(*) FROM information_schema.TABLES WHERE table_name ='"+table+"'");
            if(temp){
                deleteSqlList.add(SqlConstants.DROP_TABLE + table);
            }
        }
        insertBatch(deleteSqlList);
    }

    private void buildVersionTables(AdapterVersion baseVersion, AdapterVersion newVersion) {

        String[] newTables = {newVersion.getDataSetTableName(),
                newVersion.getMetaDataTableName(),
                newVersion.getDictTableName(),
                newVersion.getDictEntryTableName()};

        String[] baseTables = {baseVersion.getDataSetTableName(),
                baseVersion.getMetaDataTableName(),
                baseVersion.getDictTableName(),
                baseVersion.getDictEntryTableName()};

        Session session = getCurrentSession();
        for (int i = 0; i < baseTables.length; ++i) {
            String baseTable = baseTables[i];
            String newTable = newTables[i];

            Query query = session.createSQLQuery(SqlConstants.CREATE_TABLE + newTable + SqlConstants.LIKE + baseTable);
            query.executeUpdate();

            query = session.createSQLQuery(SqlConstants.INSERT_INTO + newTable + SqlConstants.SELECT + "*" + SqlConstants.FROM + baseTable);
            query.executeUpdate();
        }
    }

    private void buildVersionTables(AdapterSchemeVersionModel versionModel) throws Exception {
        AdapterVersion adapterVersion = new AdapterVersion(versionModel.getVersion());
        AdapterSchemeModel scheme = getEntity(AdapterSchemeModel.class, versionModel.getSchemeId());
        StandardVersion stdVersion = new StandardVersion(scheme.getStdVersion());
        insertBatch(createTable(adapterVersion));
        List<String> sqlList = new ArrayList<>();
        SqlCreator sqlCreator = new SqlCreator(StdMetaDataModel.class);
        String sql = sqlCreator.selectData(stdVersion.getMetaDataTableName());
        Query query = getQuery(sqlCreator, sql);
        List<StdMetaDataModel> metadataList = query.list();
        List<Integer> newDictdList = new ArrayList<>();
        for (StdMetaDataModel stdMetaDataModel : metadataList) {
            if (stdMetaDataModel.getDictId() != null && !newDictdList.contains(stdMetaDataModel.getDictId())) {
                newDictdList.add(stdMetaDataModel.getDictId());
            }
        }
        sqlList.add(insertData(adapterVersion.getDataSetTableName(), stdVersion.getDataSetTableName(),
                new String[]{"id,std_dataset_id,std_dataset_code,std_dataset_name"},
                new String[]{"id,id,code,name"},
                scheme.getId(),
                "",
                null));
        sqlList.add(insertData(adapterVersion.getMetaDataTableName(), stdVersion.getMetaDataTableName(),
                new String[]{"id,std_metadata_id,std_metadata_code,std_metadata_name,std_dataset_id,std_dict_id"},
                new String[]{"id,id,code,name,dataset_id,dict_id"},
                scheme.getId(),
                "",
                null));
        if(newDictdList.size()!=0){
            sqlList.add(insertData(adapterVersion.getDictTableName(), stdVersion.getDictTableName(),
                    new String[]{"id,std_dict_id,std_dict_code,std_dict_name"},
                    new String[]{"id,id,code,name"},
                    scheme.getId(),
                    "id",
                    newDictdList));
            sqlList.add(insertData(adapterVersion.getDictEntryTableName(), stdVersion.getDictEntryTableName(),
                    new String[]{"id,std_entry_id,std_entry_code,std_entry_value,std_dict_id"},
                    new String[]{"id,id,code,value,dict_id"},
                    scheme.getId(),
                    "dict_id",
                    newDictdList));
        }
        insertBatch(sqlList);
    }

    private List<String> createTable(AdapterVersion adapterVersion) {
        List<String> sqlList = new ArrayList<>();
        List<String> adapterTableList = new ArrayList<>();
        adapterTableList.add(adapterVersion.DataSetTablePrefix);
        adapterTableList.add(adapterVersion.MetaDataTablePrefix);
        adapterTableList.add(adapterVersion.DictTablePrefix);
        adapterTableList.add(adapterVersion.DictEntryTablePrefix);
        adapterTableList.add(adapterVersion.DatasetRelationTablePrefix);
        for (String table : adapterTableList) {
            String sql = SqlConstants.CREATE_TABLE + table + SqlConstants.UNDERLINE + adapterVersion.getVersion() + SqlConstants.LIKE + table;
            sqlList.add(sql);
        }
        return sqlList;
    }

    private String insertData(String adapterTableName, String stdTableName, String[] item1, String[] item2, Integer schemeId, String item, List<Integer> idList) {
        StringBuffer sql = new StringBuffer();
        String idStr = SqlConstants.EMPTY;
        if (!CollectionUtil.isEmpty(idList)) {
            for (Integer id : idList) {
                idStr += id + SqlConstants.COMMA;
            }
            if (!StringUtil.isEmpty(idStr)) {
                idStr = SqlConstants.LEFT_BRACKET + idStr.substring(0, idStr.length() - 1) + SqlConstants.RIGHT_BRACKET;
            }
        }

        sql.append(SqlConstants.INSERT_INTO + adapterTableName + SqlConstants.LEFT_BRACKET);
        for (String it : item1) {
            sql.append(it + SqlConstants.COMMA);
        }
        sql.append("scheme_id" + SqlConstants.RIGHT_BRACKET + SqlConstants.LEFT_BRACKET + SqlConstants.SELECT);
        for (String it : item2) {
            sql.append(it + SqlConstants.COMMA);
        }
        sql.append(schemeId + SqlConstants.FROM + stdTableName);
        if (!StringUtil.isEmpty(item)) {
            sql.append(SqlConstants.WHERE + item + SqlConstants.IN + idStr);
        }
        sql.append(SqlConstants.RIGHT_BRACKET);
        return sql.toString();
    }
    public List<AdapterSchemeVersionModel> getAll(){
        StringBuffer sql = new StringBuffer();
        sql.append("SELECT v.*").append(" FROM adapter_scheme_version v ");
        Query query = getCurrentSession().createSQLQuery(sql.toString()).addEntity(AdapterSchemeVersionModel.class);
        List<AdapterSchemeVersionModel> list = query.list();
        return list;
    }

    public List<AdapterSchemeVersionModel> getAll(String schemaName){
        StringBuffer sql = new StringBuffer();
        sql.append("SELECT v.*").append(" FROM adapter_scheme_version v ");

        String myCatAnnotation = "/*#mycat\\:schema=" + schemaName + "*/ ";
        String completeSql = myCatAnnotation + sql;

        Query query = getCurrentSession().createSQLQuery(completeSql).addEntity(AdapterSchemeVersionModel.class);
        List<AdapterSchemeVersionModel> list = query.list();
        return list;
    }
}