package me.chyxion.tigon.service.support;

import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.model.BaseModel;
import me.chyxion.tigon.mybatis.Search;
import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.mybatis.BaseMapper;
import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.StringUtils;
import me.chyxion.tigon.service.BaseCrudService;
import org.hibernate.validator.constraints.NotEmpty;
import me.chyxion.tigon.validation.annotation.NotNullOrBlank;
import org.springframework.transaction.annotation.Transactional;

/**
 * @version 0.0.1
 * @since 0.0.1
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * May 16, 2016 3:28:54 PM
 */
@Slf4j
public class BaseCrudServiceSupport 
    <PrimaryKey, Model extends BaseModel<PrimaryKey>,
        Mapper extends BaseMapper<PrimaryKey, Model>>
    extends BaseQueryServiceSupport<PrimaryKey, Model, Mapper>
    implements BaseCrudService<PrimaryKey, Model> {

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public int delete(Search search) {
        return mapper.delete(search);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int delete(PrimaryKey primaryKey) {
        return mapper.delete(primaryKey);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ViewModel<Model> create(Model model) {
        beforeInsert(model);
        mapper.insert(model);
        afterInsert(model);
        return toViewModel(model);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public Collection<ViewModel<Model>> create(@NotEmpty Collection<Model> models) {
        List<ViewModel<Model>> viewModels = new ArrayList<ViewModel<Model>>(models.size());
        for (Model model : models) {
            viewModels.add(create(model));
        }
        return viewModels;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ViewModel<Model> update(Model model) {
        beforeUpdate(model);
        mapper.update(model);
        afterUpdate(model);
        return toViewModel(model);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public int update(@NotNull Model model, @NotNull Search search) {
        return mapper.update(model, search);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int update(@NotEmpty Map<String, ?> model, @NotNullOrBlank PrimaryKey primaryKey) {
        return mapper.update(model, primaryKey);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public int update(@NotEmpty Map<String, ?> model, @NotNull Search search) {
        return mapper.update(model, search);
    }

    // --
    // private methods

    protected void beforeInsert(Model model) {
        if (idType.equals(String.class) &&
                StringUtils.isBlank((String) model.primaryKeyValue())) {
            final String id = idSeq.get();
            log.debug("Insert Model [{}] Id Is Blank, Generate [{}].", model, id);
            model.primaryKeyValue((PrimaryKey) id);
        }
        // Hook
    }

    protected void afterInsert(Model model) {
        // Hook
    }


    protected void beforeUpdate(Model model) {
        // Hook
    }

    protected void afterUpdate(Model model) {
        // Hook
    }
}
