package project.web.generic.csv;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Objects;

import batch.util.ParameterUtil;
import core.config.Factory;
import online.context.check.InputCheck;
import online.model.ModelUtil;
import online.model.UniModel;
import online.model.UniModelImpl;
import online.struts.action.BrowseAction;
import online.struts.action.UniForm;
import project.base.upload.ImportAbstract;
import project.check.attribute.LongNumeralCheck;
import project.check.attribute.NumberCheck;
import project.check.existence.MustCheck;
import project.master.MsgUtil;
import project.web.InstanceFactory;

/**
 * CSV取込アクション
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class ImportAction implements BrowseAction, CsvAction {

	/** キー項目 */
	private static final String ID = "Id";
	/** ファイル名綱目 */
	private static final String IMPORT_FILE = "ImportFile";
	/** 総件数 */
	private static final String TOTAL_COUNT = "TotalCount";
	/** 処理件数 */
	private static final String UPLOAD_COUNT = "UploadCount";
	/** 無視行 */
	private static final String IGNORE = "Ignore";
	/** エラー行 */
	private static final String ERROR = "Error";

	/**
	 * 初期表示
	 * @param model 汎用モデル
	 * @return 処理結果
	 */
	public String get(final UniForm model) {
		final var ic = InstanceFactory.create(InputCheck.class, model);
		ic.add(ID, new LongNumeralCheck());
		ic.populate();

		if (!select("project.base.upload.SelectUpload", model)) {
			if (!MsgUtil.hasTopMessage(model)) {
				if (model.hasQueryString()) {
					return ID_NG_REDIRECT;
				}
			}
		}

		return ID_VIEW;
	}

	/**
	 * アップロード
	 * @param model 汎用モデル
	 * @return 処理結果
	 */
	public String upload(final UniForm model) {
		final var ic = InstanceFactory.create(InputCheck.class, model);
		ic.add(ID, new MustCheck(), new LongNumeralCheck());
		ic.add(IMPORT_FILE, new MustCheck());
		ic.populate();

		if (!select("project.base.upload.SelectUpload", model)) {
			return ID_NG_REDIRECT;
		}

		final var sa = InstanceFactory.create(getServiceClass(model), model);
		setParameter(sa, model);
		if (sa.check()) {
			if (sa.getCount() == 0) {
				MsgUtil.putTopMessage(model, "ZZ000000013");
			} else {
				MsgUtil.putTopMessage(model, "ZZ000000020");
			}
		} else {
			model.putAll(ModelUtil.getUniModel(sa));
		}
		model.setValue(TOTAL_COUNT, sa.getCount());

		return ID_VIEW;
	}

	/**
	 * インポート
	 * @param model 汎用モデル
	 * @return 処理結果
	 */
	public String insert(final UniForm model) {
		final var ic = InstanceFactory.create(InputCheck.class, model);
		ic.add(ID, new MustCheck(), new LongNumeralCheck());
		ic.add(IMPORT_FILE, new MustCheck());
		ic.add(IGNORE, new NumberCheck());
		ic.add(ERROR, new NumberCheck());
		ic.populate();

		if (!select("project.base.upload.SelectUpload", model)) {
			return ID_NG_REDIRECT;
		}

		insertFile(model);

		update("project.base.upload.InsertUpload", model);

		return ID_OK;
	}

	/**
	 * 更新処理
	 * @param model 汎用モデル
	 */
	private void insertFile(final UniForm model) {
		final var sa = InstanceFactory.create(getServiceClass(model), model);
		setParameter(sa, model);
		model.setValue(UPLOAD_COUNT, sa.insert());
		model.setValue(TOTAL_COUNT, sa.getCount());
	}

	/**
	 * View用処理
	 * @param model 汎用モデル
	 */
	public void view(final UniForm model) {
		select("project.base.upload.SelectList", model);
	}

	/**
	 * クエリクラス取得
	 * @param <T> Type
	 * @param model 汎用モデル
	 * @return クエリクラス
	 */
	private <T extends ImportAbstract> Class<T> getServiceClass(final UniForm model) {
		return Objects.requireNonNullElseGet(
				Factory.loadClass(model.getString("Class")),
				() -> Factory.loadClass(model.getString("ImportClass")));
	}

	/**
	 * パラメタ設定
	 * @param sa インポートサービス
	 * @param model 汎用モデル
	 */
	private void setParameter(final ImportAbstract sa, final UniForm model) {
		ModelUtil.setModelValue(sa, getParameterModel(model));
		ModelUtil.setUniModel(sa, model);

		try {
			final var ff = model.getFormFile(IMPORT_FILE);
			if (!ff.isEmpty()) {
				// TODO:複数ファイル
				sa.setInputStream(ff.get(0).getInputStream());
			}
		} catch (final IOException ex) {
			throw new UncheckedIOException(ex);
		}
	}

	/**
	 * パラメタモデル取得
	 * @param model 汎用モデル
	 * @return パラメタモデル
	 */
	private UniModel getParameterModel(final UniModel model) {
		final var um = new UniModelImpl();
		ParameterUtil.toMap(ParameterUtil.toArray(model.getString("Param"))).forEach(um::setValue);
		return um;
	}
}
