package project.batch.jasper;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Objects;

import batch.util.ParameterUtil;
import common.db.JdbcSource;
import common.db.jdbc.Jdbc;
import common.sql.QueryUtil;
import core.config.Env;
import core.config.Factory;
import core.exception.PhysicalException;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRResultSetDataSource;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import project.batch.ProjectBatch;
import project.batch.ProjectPerform;

/**
 * 汎用帳票印刷バッチ
 * @author Tadashi Nakayama
 */
public final class PrintBatch extends ProjectBatch {

	/** クエリファイル名 */
	private static final String SQL = "sql";
	/** 帳票定義体 */
	private static final String TML = "jrxml";
	/** 出力ファイル名 */
	private static final String PDF = "pdf";

	/** 取得サイズデフォルト */
	private static final int DEFAULT_FETCH_SIZE = Env.getEnv("PrintBatch.FetchSize", 1000);

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

	/**
	 * @see batch.base.Batch#getBatchName()
	 */
	@Override
	public String getBatchName() {
		return getParameter().getJobName();
	}

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

		// パラメタマップ化
		final var prms = ParameterUtil.toMap(args);

		// パラメタチェック
		if (!checkParams(prms)) {
			return RET_PARAM_ERROR;
		}

		printout(prms);

		return RET_SUCCESS;
	}

	/**
	 * パラメタチェック
	 * @param prms パラメタ
	 * @return 正常の場合 true を返す。
	 */
	private boolean checkParams(final Map<String, String[]> prms) {
		return isValid(prms.get(SQL)) && isValid(prms.get(TML)) && isValid(prms.get(PDF));
	}

	/**
	 * 適正判断
	 * @param val 値配列
	 * @return 適正の場合 true を返す。
	 */
	private boolean isValid(final String[] val) {
		return val != null && val.length == 1 && !Objects.toString(val[0], "").trim().isEmpty();
	}

	/**
	 * 印刷処理
	 * @param prms パラメタ
	 */
	private void printout(final Map<String, String[]> prms) {
		try (var conn = JdbcSource.getConnection()) {
			conn.setReadOnly(true);
			conn.setAutoCommit(false);

			final var qry = getQuery(prms);
			try (var psmt = QueryUtil.createStatement(
					qry, toBindMap(prms), Jdbc.wrap(conn)::readonlyStatement)) {
				if (psmt != null) {
					if (0 < DEFAULT_FETCH_SIZE) {
						psmt.setFetchSize(DEFAULT_FETCH_SIZE);
					}
					try (ResultSet rs = psmt.executeQuery()) {
						createPdf(rs, prms);
					}
				}
			}

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

	/**
	 * PDF作成
	 * @param rs ResultSet
	 * @param prms パラメタ
	 */
	private void createPdf(final ResultSet rs, final Map<String, String[]> prms) {
		try (var is = getTemplate(prms)) {
			final var file = getOutput(prms);

			final var jr = JasperCompileManager.compileReport(is);
			final var jp = JasperFillManager.fillReport(
					jr, toParameter(prms), new JRResultSetDataSource(rs));
			JasperExportManager.exportReportToPdfFile(jp, file);

			addFile(new File(file).getCanonicalPath(), null);

		} catch (final IOException | JRException ex) {
			throw new PhysicalException(ex);
		}
	}

	/**
	 * jpパラメタ化
	 * @param map パラメタマップ
	 * @return jsパラメタ
	 */
	private Map<String, Object> toParameter(final Map<String, String[]> map) {
		return Factory.cast(map);
	}

	/**
	 * クエリ取得
	 * @param prms パラメタ
	 * @return クエリ
	 */
	private String getQuery(final Map<String, String[]> prms) {
		return QueryUtil.getSqlFromFile(prms.get(SQL)[0]);
	}

	/**
	 * 帳票定義体取得
	 * @param prms パラメタ
	 * @return 帳票定義体
	 */
	private InputStream getTemplate(final Map<String, String[]> prms) {
		var ret = prms.get(TML)[0];
		if (ret.endsWith(".jrxml")) {
			ret = ret.substring(0, ret.length() - ".jrxml".length()).replace(".", "/");
		} else {
			ret = ret.replace(".", "/");
		}

		final var cl = Thread.currentThread().getContextClassLoader();
		return cl.getResourceAsStream(ret + ".jrxml");
	}

	/**
	 * 出力先ファイル名取得
	 * @param prms パラメタ
	 * @return 出力先フィル名
	 */
	private String getOutput(final Map<String, String[]> prms) {
		var ret = prms.get(PDF)[0];
		if (!ret.endsWith(".pdf")) {
			ret = ret + ".pdf";
		}
		return ret;
	}
}
