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

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yihu.hos.web.framework.dao.SQLGeneralDAO;
import com.yihu.hos.web.framework.constant.ErrorCode;
import com.yihu.hos.web.framework.constant.SqlConstants;
import com.yihu.hos.web.framework.exception.ApiException;
import com.yihu.hos.web.framework.model.DictItem;
import com.yihu.hos.web.framework.model.DictionaryResult;
import com.yihu.hos.web.framework.model.Result;
import com.yihu.hos.core.datatype.CollectionUtil;
import com.yihu.hos.core.datatype.StringUtil;
import com.yihu.hos.web.framework.util.sql.RequestParamTransformer;
import com.yihu.hos.standard.model.adapter.AdapterSchemeModel;
import com.yihu.hos.standard.model.standard.StandardVersionModel;
import com.yihu.hos.standard.service.bo.StandardVersion;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;

/**
 * @created Airhead 2015/12/25.
 */
@Transactional
@Service("StandardVersionService")
public class StandardVersionService extends SQLGeneralDAO {

    public static final String BEAN_ID = "StandardVersionService";
    @Autowired
    private ObjectMapper objectMapper;

    public DictionaryResult getDictionaryResult(Integer standardId, String condition, String order, Integer rows, Integer page) {
        try {
            DictionaryResult dictionaryResult = DictionaryResult.success("获取标准版本成功");
            List<DictItem> detailModelList = new ArrayList<>();
            List<StandardVersionModel> standardVersionModelList = getVersionList(standardId, condition, order, rows, page);

            for(StandardVersionModel standardVersionModel : standardVersionModelList){
                DictItem items = new DictItem();
                String id = standardVersionModel.getVersion();
                String name = standardVersionModel.getName();
                items.setCode(id.toString());
                items.setValue(name);
                detailModelList.add(items);
            }
            dictionaryResult.setDetailModelList(detailModelList);
            return dictionaryResult;
        } catch (Exception e) {
            return DictionaryResult.error("获取标准版本失败");
        }
    }

    public DictionaryResult getDetailResultModelList(Integer standardId, String condition, String order, Integer rows, Integer page) {
        try {
            List<StandardVersionModel> standardVersionModelList = getVersionList(standardId, condition, order, rows, page);
            DictionaryResult dictionaryResult = new DictionaryResult();
            List<DictItem> detailModelList = new ArrayList<DictItem>();
//            DictItem item = new DictItem();
//            item.setCode("");
//            item.setValue("-无-");
//            detailModelList.add(item);
            for (StandardVersionModel standardVersionModel : standardVersionModelList) {
                DictItem items = new DictItem();
                String  id = standardVersionModel.getVersion();
                String name = standardVersionModel.getName();
                items.setCode(id.toString());
                items.setValue(name);
                detailModelList.add(items);
            }
            dictionaryResult.setDetailModelList(detailModelList);
            dictionaryResult.setSuccessFlg(true);
            dictionaryResult.setMessage("获取标准版本列表成功");
            return dictionaryResult;
        } catch (Exception e) {
            return DictionaryResult.error("获取标准版本列表失败");
        }
    }

    public List<StandardVersionModel> getVersionList(Integer standardId, String condition, String order, Integer limit, Integer offset) {
        try {
            Map queryMap = RequestParamTransformer.parseJsonToMap(condition);
            Map orderMap = RequestParamTransformer.parseJsonToMap(order);
            return this.getList(standardId, queryMap, orderMap, limit, offset);
        } catch (Exception e) {
            e.printStackTrace();
        }

        throw new ApiException(ErrorCode.StandardVersionGetListFailed);
    }

    public Result add(String version) throws Exception {
        Boolean existFlg = false;
        try {
            StandardVersionModel versionModel = objectMapper.readValue(version, StandardVersionModel.class);
            existFlg = versionNameValidate(versionModel.getStandardId(), versionModel.getName());
            if (!existFlg) {
                this.add(versionModel);
                versionModel.setSuccessFlg(true);
                versionModel.setMessage("保存标准版本成功");
                return versionModel;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (existFlg) {
            return Result.error("已存在标准版本名称");
        } else {
            return Result.error("保存标准版本失败");
        }
    }

    public Boolean versionNameValidate(Integer standardId, String versionName) {
            Map<String, Object> query = new HashMap<>();
            Map<String, String> order = new HashMap<>();
            query.put("name", versionName);
            List<StandardVersionModel> list = getList(standardId, query, order, 1, 0);
            if (!CollectionUtil.isEmpty(list)) {
                return true;
            }
            return false;
    }

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

    public Result delete(Integer versionId,String version) throws Exception {
        Boolean existFlg = false;
        Boolean useredFlg = false;
        try {
            StandardVersionModel versionModel = (StandardVersionModel) get(versionId);
            if (versionModel.getPublishStatus() == 1) {
                existFlg = true;
            } else {
                String sql = "FROM AdapterSchemeModel WHERE stdVersion = :stdVersion or adapterStdVersion =  :adapterStdVersion";
                Query query = getCurrentSession().createQuery(sql);
                query.setParameter("stdVersion", version);
                query.setParameter("adapterStdVersion", version);
                List<AdapterSchemeModel> list = query.list();
                if (!CollectionUtil.isEmpty(list)) {
                    useredFlg = true;
                }else{
                    this.delete(versionModel);
                    deleteVersionTables(new StandardVersion(versionModel.getVersion()));
                    return Result.success("删除标准版本成功");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (existFlg) {
            return Result.error("该标准版本已发布");
        } else if(useredFlg){
            return Result.error("该标准版本已被适配使用");
        } else {
            return Result.error("删除标准版本失败");
        }
    }

    public Result update(String version) throws Exception {
        Boolean existFlg = false;
        try {
            StandardVersionModel versionModel = objectMapper.readValue(version, StandardVersionModel.class);
            existFlg = versionNameValidate(versionModel.getStandardId(), versionModel.getId(), versionModel.getName());
            if (!existFlg) {
                this.update(versionModel);
                versionModel.setSuccessFlg(true);
                versionModel.setMessage("修改标准版本成功");
                return versionModel;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (existFlg) {
            return Result.error("已存在标准版本名称");
        } else {
            return Result.error("修改标准版本失败");
        }
    }

    public Result get(Integer versionId) {
        try {
            StandardVersionModel versionModel = this.myGet(versionId);
            versionModel.setSuccessFlg(true);
            versionModel.setMessage("获取标准版本信息成功");
            return versionModel;
        } catch (DataAccessException e) {
            return Result.error("获取标准版本信息失败");
        }
    }

    public StandardVersionModel fork(Integer versionId) throws Exception {
        try {
            return this.myFork(versionId);

        } catch (DataAccessException | IOException e) {
            throw new ApiException(ErrorCode.StandardVersionForkFailed);
        }
    }

    public Result publish(Integer versionId, String publisher) throws Exception {
        try {
            StandardVersionModel standardVersionModel = this.myPublish(versionId, publisher);
            standardVersionModel.setSuccessFlg(true);
            standardVersionModel.setMessage("发布标准版本成功");
            return standardVersionModel;
        } catch (DataAccessException e) {
            return Result.error("发布标准版本失败");
        }
    }

    public StandardVersionModel getLastestVersion(Integer standardId) {
        Map<String, Object> query = new HashMap<>();
        Map<String, String> order = new HashMap<>();
        order.put("publisher", SqlConstants.DESC);

        List<StandardVersionModel> list = getList(standardId, query, order, 1, 0);
        if (list != null && list.size() != 0) {
            return list.get(0);
        }

        return null;
    }

    public StandardVersionModel myPublish(Integer versionId, String publisher) throws Exception {
        StandardVersionModel standardVersionModel = myGet(versionId);
        //TODO:generate standard version file;
        String path = "";
        standardVersionModel.setPath(path);
        standardVersionModel.setPublishTime(new Date());
        standardVersionModel.setPublishUser(publisher);
        standardVersionModel.setPublishStatus(1);

        this.update(standardVersionModel);

        return standardVersionModel;
    }

    private StandardVersionModel myFork(Integer versionId) throws Exception {
        StandardVersionModel baseVersionModel = myGet(versionId);
        StandardVersionModel newVersionModel = new StandardVersionModel();
        newVersionModel.setBaseVersion(baseVersionModel.getVersion());
        newVersionModel.setName(baseVersionModel.getName() + " Copy");
        newVersionModel.setPrimaryVersion(baseVersionModel.getPrimaryVersion());
        newVersionModel.setPublishTime(new Date());
        this.add(newVersionModel);

        StandardVersion baseVersion = new StandardVersion(baseVersionModel.getVersion());
        StandardVersion newVersion = new StandardVersion(newVersionModel.getVersion());
        buildVersionTables(baseVersion, newVersion, baseVersionModel.getStandardId());

        return newVersionModel;
    }

    private StandardVersionModel myGet(Integer versionId) {
        try {
            return (StandardVersionModel) getEntity(StandardVersionModel.class, versionId);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void delete(StandardVersionModel standardVersionModel) throws Exception {
        deleteEntity(standardVersionModel);
    }

    private void update(StandardVersionModel standardVersionModel) throws Exception {
        try {
            updateEntity(standardVersionModel);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void  add(StandardVersionModel standardVersionModel) throws Exception {
        StandardVersion standardVersion = StandardVersion.generateVersion();
        standardVersionModel.setVersion(standardVersion.getVersion());
        standardVersionModel.setPublishTime(null);
        standardVersionModel.setPublishStatus(0);
        saveEntity(standardVersionModel);

        String baseVer = standardVersionModel.getBaseVersion();
        StandardVersion baseVersion = StringUtil.isEmpty(baseVer) ? null : new StandardVersion(baseVer);
        StandardVersion newVersion = new StandardVersion(standardVersionModel.getVersion());
        buildVersionTables(baseVersion, newVersion, standardVersionModel.getStandardId());
    }

    private List<StandardVersionModel> getList(Integer standardId, Map<String, Object> query, Map<String, String> order, Integer limit, Integer offset) {
        Session session = getCurrentSession();
        Criteria criteria = session.createCriteria(StandardVersionModel.class);
        criteria.add(Restrictions.eq("standardId", standardId));
        if (query != null) {
            for (String key : query.keySet()) {
                criteria.add(Restrictions.eq(key, query.get(key)));
            }
        }
        if (limit != null) {
            criteria.setMaxResults(limit);
            if (offset != null) {
                criteria.setFirstResult((offset - 1) * limit);
            }
        }
        if (order != null) {
            for (String key : order.keySet()) {
                if (order.get(key).equals(SqlConstants.ASC)) {
                    criteria.addOrder(Order.asc(key));
                } else if (order.get(key).equals(SqlConstants.DESC)) {
                    criteria.addOrder(Order.desc(key));
                }
            }
        }
        return (List<StandardVersionModel>) criteria.list();
    }

    private void deleteVersionTables(StandardVersion version) {
        String[] tables = {version.getDataSetTableName(),
                version.getMetaDataTableName(),
                version.getDictTableName(),
                version.getDictEntryTableName()
                /*version.getCDATableName(),
               /* version.getCDADatasetTableName()*/};
        List<String> deleteSql = new ArrayList<>();
        for (int i = 0; i < tables.length; ++i) {
            String table = tables[i];
            deleteSql.add(SqlConstants.DROP_TABLE + table);
        }
        insertBatch(deleteSql);
    }

    private void buildVersionTables(StandardVersion baseVersion, StandardVersion newVersion, Integer standardId) {

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

        String[] baseTables;
        if (baseVersion == null) {
            String[] templateTables = {StandardVersion.DataSetTable,
                    StandardVersion.MetaDataTable,
                    StandardVersion.DictTable,
                    StandardVersion.DictEntryTable
                   /* StandardVersion.CDADocumentTable,
                    StandardVersion.CDADatasetTable*/};
            baseTables = templateTables;
        } else {
            String[] templateTables = {baseVersion.getDataSetTableName(),
                    baseVersion.getMetaDataTableName(),
                    baseVersion.getDictTableName(),
                    baseVersion.getDictEntryTableName()
                    /*baseVersion.getCDATableName(),
                    baseVersion.getCDADatasetTableName()*/};
            baseTables = templateTables;
        }

        List<String> createSql = new ArrayList<>();
        List<String> insertSql = new ArrayList<>();
        List<String> updateSql = new ArrayList<>();
        for (int i = 0; i < baseTables.length; ++i) {
            String baseTable = baseTables[i];
            String newTable = newTables[i];
            createSql.add(SqlConstants.CREATE_TABLE + newTable + SqlConstants.LIKE + baseTable);
        }

        for (int i = 0; i < baseTables.length; ++i) {
            String baseTable = baseTables[i];
            String newTable = newTables[i];
            insertSql.add(SqlConstants.INSERT_INTO + newTable + SqlConstants.SELECT + SqlConstants.ASTERISK + SqlConstants.FROM + baseTable);
        }

        for (int i = 0; i < baseTables.length; ++i) {
            String newTable = newTables[i];
            updateSql.add(SqlConstants.UPDATE + newTable + SqlConstants.SET + " STD_ID = " + standardId);
        }
        insertBatch(createSql);
        insertBatch(insertSql);
        insertBatch(updateSql);
    }
}
