package batch.status;

import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.logging.log4j.LogManager;

import common.db.jdbc.Jdbc;
import common.sql.QueryUtil;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;
import core.util.bean.Pair;

/**
 * ジョブファイル管理テーブル取得／更新
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public class JobFileStatusImpl implements JobFileStatus {

	/**
	 * ファイル管理レコード取得
	 *
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @param conn コネクション
	 * @param query クエリ
	 * @return レコード情報
	 */
	private JobFile getJobFile(final long jseq, final int dseq,
			final int fseq, final Connection conn, final String query) {
		final Map<String, Object> map = new HashMap<>();
		map.put("JobSeq", Long.valueOf(jseq));
		map.put("BatSeq", Integer.valueOf(dseq));
		map.put("FileSeq", Integer.valueOf(fseq));

		try (PreparedStatement psmt = QueryUtil.createStatement(
				query, map, Jdbc.wrap(conn)::readonlyStatement)) {
			// ジョブ管理テーブル読み込み
			JobFile ret = null;
			try (ResultSet rs = psmt.executeQuery()) {
				if (rs.next()) {
					ret = toJobFileObject(rs);
				}
			}
			return ret;

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

	/**
	 * ファイル管理レコードリスト取得
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return レコードリスト
	 */
	@Override
	public List<JobFile> selectJobFiles(final Connection conn, final long seq) {

		final String query = QueryUtil.getSqlFromFile("SelectJobFileList", this.getClass());
		try (PreparedStatement psmt = QueryUtil.createStatement(
				query, Collections.singletonMap("JobSeq", Long.valueOf(seq)),
				Jdbc.wrap(conn)::readonlyStatement)) {
			final List<JobFile> ret = new ArrayList<>();
			try (ResultSet rs = psmt.executeQuery()) {
				while (rs.next()) {
					ret.add(toJobFileObject(rs));
				}
			}
			return ret;

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

	/**
	 * ファイル管理レコード更新
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @param count ダウンロード回数
	 * @param size ファイルサイズ
	 * @return 更新件数
	 */
	@Override
	public int updateJobFile(final Connection conn, final long jseq, final int dseq,
			final int fseq, final int count, final long size) {
		final Map<String, Object> map = new HashMap<>();
		map.put("JobSeq", Long.valueOf(jseq));
		map.put("BatSeq", Integer.valueOf(dseq));
		map.put("FileSeq", Integer.valueOf(fseq));
		map.put("Count", Integer.valueOf(count));
		map.put("FileSize", Long.valueOf(size));

		final String query = QueryUtil.getSqlFromFile("UpdateJobFile", this.getClass());
		try (PreparedStatement psmt = QueryUtil.createStatement(
				query, map, Jdbc.wrap(conn)::prepareStatement)) {
			return psmt.executeUpdate();
		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ファイル管理削除
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return 削除件数
	 */
	@Override
	public int deleteJobFile(final Connection conn, final long seq) {
		final String query = QueryUtil.getSqlFromFile("DeleteJobFile", this.getClass());
		try (PreparedStatement psmt = QueryUtil.createStatement(
				query, Collections.singletonMap("JobSeq", Long.valueOf(seq)),
				Jdbc.wrap(conn)::prepareStatement)) {
			// ファイル管理削除
			return psmt.executeUpdate();
		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ファイル管理レコード新規作成
	 *
	 * @param conn コネクション
	 * @param jfile ジョブファイル
	 * @return 作成件数
	 */
	@Override
	public int insertJobFile(final Connection conn, final JobFile jfile) {
		final Map<String, Object> map = new HashMap<>();
		map.put("JobSeq", Long.valueOf(jfile.getJobSeq()));
		map.put("BatSeq", Integer.valueOf(jfile.getBatSeq()));
		map.put("FileSeq", Integer.valueOf(jfile.getFileSeq()));
		map.put("FileName", Integer.valueOf(jfile.getFileName()));
		map.put("FilePath", jfile.getPathName());
		map.put("FileSize", Long.valueOf(jfile.getFileSize()));
		map.put("DownloadName", jfile.getDownloadName());
		map.put("Count", Integer.valueOf(0));

		final String query = QueryUtil.getSqlFromFile("InsertJobFile", this.getClass());
		try (PreparedStatement psmt = QueryUtil.createStatement(
				query, map, Jdbc.wrap(conn)::prepareStatement)) {
			// ファイル管理テーブル更新
			return psmt.executeUpdate();
		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * バッチ生成ファイル追加
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param dtlno バッチ連番
	 * @param list 作成ファイルリスト
	 * @return 作成レコード数
	 */
	@Override
	public int setFiles(final Connection conn, final long seq,
			final int dtlno, final List<Pair<String, String>> list) {

		int ret = 0;
		if (list != null) {
			int fseq = 1;
			for (final Pair<String, String> strs : list) {
				// フルパスファイル名取得
				final String fullpath = strs.left();
				if (fullpath == null) {
					continue;
				}

				final File f = new File(fullpath);

				int cnt = 0;
				// ファイル管理テーブル更新
				JobFile info = getJobFileWithLock(conn, seq, dtlno, fseq);
				if (info == null) {
					info = new JobFile();
					info.setJobSeq(seq);
					info.setBatSeq(dtlno);
					info.setFileSeq(fseq);
					info.setFileName(f.getName());
					info.setPathName(new File(f.getParent()).toURI().getPath());
					info.setFileSize(f.length());
					info.setDownloadName(strs.right());
					cnt = insertJobFile(conn, info);
				} else {
					cnt = updateJobFile(conn, seq, dtlno, fseq,
								info.getDownloadCount(), f.length());
				}

				if (cnt != 1) {
					throw new IllegalStateException(String.valueOf(cnt));
				}

				fseq++;
				ret += cnt;
			}
		}
		return ret;
	}

	/**
	 * ファイル削除
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return 処理結果数
	 */
	@Override
	public int deleteFiles(final Connection conn, final long seq) {
		int rmfile = 0;
		final List<JobFile> l = selectJobFiles(conn, seq);
		if (!l.isEmpty()) {
			for (final JobFile jf : l) {
				if (removeFile(jf.getFileName(), jf.getPathName())) {
					rmfile++;
				}
			}
		}
		return rmfile;
	}

	/**
	 * ファイル削除処理
	 *
	 * @param fname ファイル名
	 * @param path パス名
	 * @return 正常に削除された場合 true
	 */
	private boolean removeFile(final String fname, final String path) {
		if (Objects.toString(fname, "").trim().isEmpty()) {
			return false;
		}

		final String p = Objects.toString(path, "");
		// 作成ファイルの削除
		LogManager.getLogger().warn("remove path={} file={}", p, fname);
		final File f = new File(p, fname);
		return f.delete();
	}

	/**
	 * ファイル管理テーブルのダウンロード回数を更新
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @return 取得項目値
	 */
	@Override
	public JobFile countUpDownload(final Connection conn, final long jseq,
			final int dseq, final int fseq) {
		// ファイル管理情報取得
		final JobFile ret = getJobFileWithLock(conn, jseq, dseq, fseq);
		if (ret != null) {
			final int count = ret.getDownloadCount();
			final long size = ret.getFileSize();
			final int cnt = updateJobFile(conn, jseq, dseq, fseq, count + 1, size);
			if (cnt != 1) {
				throw new IllegalStateException(String.valueOf(cnt));
			}
		}
		return ret;
	}

	/**
	 * ファイル管理レコード取得
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @return レコード情報
	 */
	@Override
	public JobFile getJobFileWithLock(final Connection conn, final long jseq,
			final int dseq, final int fseq) {
		final String query = QueryUtil.getSqlFromFile("SelectJobFileWithLock", this.getClass());
		return getJobFile(jseq, dseq, fseq, conn, query);
	}

	/**
	 * ファイル管理レコード取得
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @return レコード情報
	 */
	@Override
	public JobFile getJobFile(final Connection conn, final long jseq,
			final int dseq, final int fseq) {
		final String query = QueryUtil.getSqlFromFile("SelectJobFile", this.getClass());
		return getJobFile(jseq, dseq, fseq, conn, query);
	}

	/**
	 * ジョブファイル管理オブジェクト取得
	 *
	 * @param rs 結果セット
	 * @return ジョブファイル管理
	 * @throws SQLException SQL例外
	 */
	private JobFile toJobFileObject(final ResultSet rs) throws SQLException {
		final JobFile ret = new JobFile();
		ret.setJobSeq(rs.getLong("JOB_SEQ"));
		ret.setBatSeq(rs.getInt("BAT_SEQ"));
		ret.setFileSeq(rs.getInt("FILE_SEQ"));
		ret.setFileName(rs.getString("FILE_NAME"));
		ret.setPathName(rs.getString("FILE_PATH"));
		ret.setFileSize(rs.getLong("FILE_SIZE"));
		ret.setDownloadName(rs.getString("DOWNLOAD_NAME"));
		ret.setDownloadCount(rs.getInt("DOWNLOAD_COUNT"));
		return ret;
	}
}
