package me.chyxion.tigon.service.support;

import java.util.*;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.mybatis.Search;
import me.chyxion.tigon.model.BaseModel;
import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.mybatis.BaseMapper;
import org.apache.commons.lang3.StringUtils;
import me.chyxion.tigon.service.BaseCrudService;
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) {
        return create(Arrays.asList(model)).iterator().next();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public Collection<ViewModel<Model>> create(Collection<Model> models) {
        for (final Model model : models) {
            model.beforeInsert();
            beforeInsert(model);
        }
        mapper.insert(models);
        final List<ViewModel<Model>> viewModels =
            new ArrayList<>(models.size());
        for (Model model : models) {
            afterInsert(model);
            viewModels.add(toViewModel(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(Model model, Search search) {
        return mapper.update(model, search);
    }

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

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public int update(Map<String, ?> model, 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
    }
}
