/**
 * ***************************************************************************
 * Copyright (c) 2010 Qcadoo Limited
 * Project: Qcadoo MES
 * Version: 1.4
 *
 * This file is part of Qcadoo.
 *
 * Qcadoo is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation; either version 3 of the License,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * ***************************************************************************
 */
package com.qcadoo.mes.states.service.client;

import static com.qcadoo.mes.states.messages.util.MessagesUtil.*;
import static org.springframework.context.i18n.LocaleContextHolder.getLocale;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.qcadoo.localization.api.TranslationService;
import com.qcadoo.mes.states.StateChangeContext;
import com.qcadoo.mes.states.StateChangeEntityDescriber;
import com.qcadoo.mes.states.constants.StateChangeStatus;
import com.qcadoo.mes.states.exception.StateChangeException;
import com.qcadoo.mes.states.messages.util.MessagesUtil;
import com.qcadoo.mes.states.service.StateChangeContextBuilder;
import com.qcadoo.mes.states.service.StateChangeService;
import com.qcadoo.mes.states.service.StateChangeServiceResolver;
import com.qcadoo.model.api.Entity;

@Service
public class StateChangeSamplesClientImpl implements StateChangeSamplesClient {

    @Autowired
    private StateChangeServiceResolver stateChangeServiceResolver;

    @Autowired
    private StateChangeContextBuilder stateChangeContextBuilder;

    @Autowired
    private TranslationService translationService;

    private static final Logger LOGGER = LoggerFactory.getLogger(StateChangeSamplesClient.class);

    @Override
    public Entity changeState(final Entity entity, final String targetState) {
        Entity resultEntity = null;
        final StateChangeService stateChangeService = stateChangeServiceResolver.get(entity.getDataDefinition());
        if (stateChangeService == null) {
            resultEntity = performDummyChange(entity, targetState);
        } else {
            resultEntity = performChange(stateChangeService, entity, targetState);
        }
        return resultEntity;
    }

    @Override
    public void resumeStateChange(final Entity entity, final Entity stateChangeEntity) {
        entity.getDataDefinition().save(entity);
        final StateChangeService stateChangeService = stateChangeServiceResolver.get(entity.getDataDefinition());
        if (stateChangeService != null) {
            final StateChangeEntityDescriber describer = stateChangeService.getChangeEntityDescriber();
            final StateChangeContext stateChangeContext = stateChangeContextBuilder.build(describer, stateChangeEntity);
            stateChangeContext.setStatus(StateChangeStatus.IN_PROGRESS);
            stateChangeService.changeState(stateChangeContext);
            checkResults(stateChangeContext);
        }
    }

    private Entity performChange(final StateChangeService stateChangeService, final Entity entity, final String targetState) {
        final StateChangeEntityDescriber describer = stateChangeService.getChangeEntityDescriber();
        final StateChangeContext stateChangeContext = stateChangeContextBuilder.build(describer, entity, targetState);
        stateChangeService.changeState(stateChangeContext);
        checkResults(stateChangeContext);

        Entity resultEntity = null;
        if (entity.getId() == null) {
            resultEntity = entity.getDataDefinition().save(entity);
        } else {
            resultEntity = entity.getDataDefinition().get(entity.getId());
        }

        return resultEntity;
    }

    private Entity performDummyChange(final Entity entity, final String targetState) {
        // fire onUpdate hooks
        entity.getDataDefinition().save(entity);
        entity.setField("state", targetState);
        return entity.getDataDefinition().save(entity);
    }

    private void checkResults(final StateChangeContext stateChangeContext) {
        checkValidationErrors(stateChangeContext);
        if (StateChangeStatus.FAILURE.equals(stateChangeContext.getStatus())) {
            throw new StateChangeException("State change failed");
        }
    }

    private void checkValidationErrors(final StateChangeContext stateChangeContext) {
        final List<Entity> messages = stateChangeContext.getAllMessages();
        if (!MessagesUtil.hasValidationErrorMessages(messages)) {
            return;
        }
        if (LOGGER.isErrorEnabled()) {
            logValidationMessages(messages);
        }
        throw new StateChangeException("Entity has validation errors.");
    }

    private void logValidationMessages(final List<Entity> messages) {
        final StringBuilder logMessage = new StringBuilder();
        logMessage.append("State change failed due to validation errors:\n");
        for (Entity message : messages) {
            logMessage.append("\t- ");
            final String correspondingFieldName = getCorrespondFieldName(message);
            if (!StringUtils.isBlank(correspondingFieldName)) {
                logMessage.append(correspondingFieldName);
                logMessage.append(": ");
            }
            logMessage.append(translationService.translate(getKey(message), getLocale(), getArgs(message)));
            logMessage.append("\n");
        }
        LOGGER.error(logMessage.toString());
    }

}
