package project.master;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

import common.db.JdbcSource;
import common.db.jdbc.Jdbc;
import common.master.IntervalTimer.IntervalCache;
import common.sql.QueryUtil;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;

/**
 * コードマスタ取得保持実装
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class CodeKeeperImpl implements CodeKeeper, IntervalCache {

	/** 自身保持用 */
	private static final AtomicReference<CodeKeeperImpl> INSTANCE = new AtomicReference<>();

	/** 格納用ハッシュ */
	private final ConcurrentMap<String, List<Code>> code = new ConcurrentHashMap<>();

	/**
	 * コンストラクタ
	 */
	private CodeKeeperImpl() {
		if (INSTANCE.get() != null) {
			throw new AssertionError();
		}
	}

	/**
	 * インスタンス取得
	 *
	 * @return インスタンス
	 */
	public static CodeKeeperImpl getInstance() {
		if (INSTANCE.get() == null) {
			INSTANCE.compareAndSet(null, new CodeKeeperImpl());
		}
		return INSTANCE.get();
	}

	/**
	 * 削除処理
	 *
	 * @param kbn コード種別
	 */
	@Override
	public void remove(final String kbn) {
		this.code.remove(kbn);
	}

	/**
	 * 選択項目取得
	 *
	 * @param kbn コード種別
	 * @param cd コード
	 * @return コードマスタ
	 */
	@Override
	public Code getCode(final String kbn, final String cd) {
		final List<Code> l = getCodeList(kbn);
		if (l != null && cd != null) {
			for (final Code c : l) {
				if (c.getCode().equals(cd)) {
					return c;
				}
			}
		}
		return null;
	}

	/**
	 * マスタ情報の存在と有効を確認
	 *
	 * @param kbn コード種別
	 * @param cd コード
	 * @return 存在し有効な場合 true
	 */
	@Override
	public boolean isValidCode(final String kbn, final String cd) {
		return getCode(kbn, cd) != null;
	}

	/**
	 * コードリスト取得
	 *
	 * @param kbn コード種別
	 * @return コードリスト
	 */
	private List<Code> setCodeList(final String kbn) {
		try (Jdbc conn = JdbcSource.getConnection()) {
			final String query = QueryUtil.getSqlFromFile("SEL_MS_CODE", this.getClass());
			try (PreparedStatement psmt = QueryUtil.createStatement(
					query, Collections.singletonMap("SbCode", kbn),
					Jdbc.wrap(conn)::readonlyStatement)) {

				List<Code> l = null;
				try (ResultSet rs = psmt.executeQuery()) {
					while (rs.next()) {
						final Code cd = new Code(
								rs.getString(1), rs.getString(2),
								rs.getString(3), rs.getString(4),
								rs.getString(5), rs.getString(6),
								rs.getString(7));
						if (l == null) {
							l = new ArrayList<>();
						}
						l.add(cd);
					}
				}
				return l;
			}
		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * コードマスタリスト取得
	 *
	 * @param kbn コード種別
	 * @return コードマスタリスト
	 */
	@Override
	public List<Code> getCodeList(final String kbn) {
		List<Code> l = this.code.get(kbn);
		if (l == null) {
			l = setCodeList(kbn);
			if (l != null) {
				this.code.putIfAbsent(kbn, Collections.unmodifiableList(l));
			} else {
				this.code.putIfAbsent(kbn, Collections.EMPTY_LIST);
			}
		}
		return l;
	}

	/**
	 * 初期処理を行う。
	 *
	 */
	@Override
	public void initialize() {
		this.code.clear();
	}
}
