package project.batch.generic.csv;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Map;
import java.util.Objects;

import org.apache.logging.log4j.LogManager;

import batch.util.ParameterUtil;
import common.db.JdbcSource;
import core.config.Factory;
import core.exception.PhysicalException;
import core.file.ZippedLineOutputStream;
import core.util.MojiUtil;
import project.batch.ProjectBatch;
import project.batch.ProjectPerform;
import project.common.CsvUtil;
import project.common.master.AppConfig;

/**
 * 汎用CSV一括出力
 * @author Tadashi Nakayama
 */
public final class ExtractBatch extends ProjectBatch {

	/**
	 * メイン処理
	 * @param args 引数
	 */
	public static void main(final String... args) {
		final var ret = ProjectPerform.start(ExtractBatch.class, args);
		Runtime.getRuntime().exit(ret);
	}

	/**
	 * @see batch.base.Batch#getBatchName()
	 */
	@Override
	public String getBatchName() {
		return "汎用CSV一括出力";
	}

	/**
	 * @see batch.base.Batch#perform(java.lang.String...)
	 */
	@Override
	public int perform(final String... args) {

		final var params = ParameterUtil.toMap(args);

		if (!ParameterUtil.hasValue("UserPatternId", params)) {
			return RET_ENV_ERROR;
		}

		final var pattern = params.remove("UserPatternId");

		// ディレクトリ名
		final var ac = Factory.create(AppConfig.class);
		final var path = ac.getValue("ExtractBatch", "DIRECTORY", "ZIP");
		if (Objects.toString(path, "").isEmpty() || !new File(path).isDirectory()) {
			return RET_ENV_ERROR;
		}

		File file = null;
		try {
			file = File.createTempFile("ExtractBatch", ".zip", new File(path));

			outputZip(file, pattern[0], params);

			addFile(file.getCanonicalPath(), getParameter().getJobName() + ".zip");

			return RET_SUCCESS;

		} catch (final IOException ex) {
			if (file != null) {
				file.deleteOnExit();
			}
			LogManager.getLogger().error(ex.getMessage(), ex);
			return RET_ENV_ERROR;
		}
	}

	/**
	 * Zip出力
	 * @param file Zipファイル
	 * @param pattern ユーザパターンID
	 * @param params パラメタ
	 * @throws IOException IO例外
	 */
	private void outputZip(final File file, final String pattern,
			final Map<String, String[]> params) throws IOException {
		try (var fos = new FileOutputStream(file);
			var bos = new BufferedOutputStream(fos);
			var zlos = new ZippedLineOutputStream(bos, MojiUtil.CHARSET_W31J)
		) {
			final var bind = toBindMap(params);
			for (final var ptn : pattern.split(",")) {
				bind.put("Id", ptn);

				final var bd = new ExtractBatchBuilder();
				bd.setParam(bind);
				if (!bd.build()) {
					LogManager.getLogger().fatal("パターン取得失敗:[{}]", bind.get("Id"));
				} else {
					LogManager.getLogger().fatal("パターン取得:[{}]", bd.getNmUserPattern());

					setOutputInfo(zlos, bd);
					output(zlos, bd, bind);
				}
			}
			zlos.finish();
		}
	}

	/**
	 * 出力情報設定
	 * @param zlos ZippedLineOutputStream
	 * @param bd ExtractBatchBuilder
	 */
	private void setOutputInfo(final ZippedLineOutputStream zlos, final ExtractBatchBuilder bd) {
		if (bd.isBunkatsu()) {
			zlos.setLine(60000);
		} else {
			zlos.setLine(-1);
		}
		zlos.setTitle(CsvUtil.toByteTitle(bd.getTitle(), StandardCharsets.UTF_8));
		zlos.setEntryName(bd.getNmUserPattern() + ".csv");
	}

	/**
	 * 出力処理
	 * @param zlos ZippedLineOutputStream
	 * @param bd ExtractBatchBuilder
	 * @param bind パラメタ
	 */
	private void output(final ZippedLineOutputStream zlos,
			final ExtractBatchBuilder bd, final Map<String, Object> bind) {
		try (var conn = JdbcSource.getConnection(bd.getSchema())) {
			conn.setAutoCommit(false);

			final var index = bd.getPageBreakIndex();
			final var query = create(ExtractBatchQuery.class);
			if (index.length == 0) {
				query.setOutputStream(zlos);
			} else {
				query.setOutputStream(toCsvPageBreak(zlos, index));
			}
			query.setQuery(bd.getQuery());
			query.setCharset(StandardCharsets.UTF_8);
			query.setTitle(bd.getTitle());
			query.setParam(bind);
			query.execute(conn);

			conn.rollback();
		} catch (final SQLException ex) {
			throw new PhysicalException(ex);
		}
	}

	/**
	 * CsvPageBreakOutputStream化
	 * @param zlos ZippedLineOutputStream
	 * @param index 改頁項目位置
	 * @return CsvPageBreakOutputStream
	 */
	private OutputStream toCsvPageBreak(final ZippedLineOutputStream zlos, final int[] index) {
		return Factory.construct(Factory.getConstructor(
				ZippedLineOutputStream.class, int[].class, Charset.class),
				zlos, index, StandardCharsets.UTF_8);
	}
}
