package common.db.dao;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

/**
 * DAO
 *
 * @author Tadashi Nakayama
 */
public interface Dao extends AutoCloseable {

	/**
	 * プライマリキーによる排他検索
	 *
	 * @param <T> ジェネリックス
	 * @param cls 読込モデルのクラス
	 * @param id プライマリキーオブジェクト
	 * @return 検索結果オブジェクト。対象なしの場合 null を返す。
	 */
	<T> T findByIdWithLock(Class<T> cls, Serializable id);

	/**
	 * プライマリキーによる検索
	 *
	 * @param <T> ジェネリックス
	 * @param cls 読込モデルのクラス
	 * @param id プライマリキーオブジェクト
	 * @return 検索結果オブジェクト。対象なしの場合 null を返す。
	 */
	<T> T findById(Class<T> cls, Serializable id);

	/**
	 * レコード挿入
	 *
	 * @param item 挿入対象モデル
	 */
	void insert(Serializable item);

	/**
	 * レコード追加または更新
	 *
	 * @param item 更新対象モデル
	 */
	void merge(Serializable item);

	/**
	 * レコード更新
	 *
	 * @param item 更新対象モデル
	 * @return 更新された場合 true を返す。対象なしの場合 false を返す。
	 */
	boolean update(Serializable item);

	/**
	 * レコード削除
	 *
	 * @param item 削除対象モデル
	 * @return 削除された場合 true を返す。対象なしの場合 false を返す。
	 */
	boolean delete(Serializable item);

	/**
	 * ネイティブSQLクエリ実行
	 *
	 * @param <T> ジェネリックス
	 * @param cls 返却クラスタイプ
	 * @param query SQL
	 * @param vals パラメタ値
	 * @return クエリオブジェクト
	 */
	default <T> T select(Class<T> cls, String query, Object... vals) {
		final Map<String, Object> map = Stream.of(vals).collect(HashMap::new,
				(m, v) -> m.put(String.valueOf(m.size() + 1), v), HashMap::putAll);
		return select(cls, query, map);
	}

	/**
	 * ネイティブSQLクエリ実行
	 *
	 * @param <T> ジェネリックス
	 * @param cls 返却クラスタイプ
	 * @param query SQL
	 * @param map パラメタ値マップ
	 * @return クエリオブジェクト
	 */
	<T> T select(Class<T> cls, String query, Map<String, ?> map);

	/**
	 * ネイティブSQLクエリ実行（排他）
	 *
	 * @param <T> ジェネリックス
	 * @param cls 返却クラスタイプ
	 * @param query SQL
	 * @param vals パラメタ値
	 * @return クエリオブジェクト
	 */
	default <T> T selectWithLock(Class<T> cls, String query, Object... vals) {
		final Map<String, Object> map = Stream.of(vals).collect(HashMap::new,
				(m, v) -> m.put(String.valueOf(m.size() + 1), v), HashMap::putAll);
		return selectWithLock(cls, query, map);
	}

	/**
	 * ネイティブSQLクエリ実行（排他）
	 *
	 * @param <T> ジェネリックス
	 * @param cls 返却クラスタイプ
	 * @param query SQL
	 * @param map パラメタ値マップ
	 * @return クエリオブジェクト
	 */
	<T> T selectWithLock(Class<T> cls, String query, Map<String, ?> map);

	/**
	 * ネイティブSQLクエリ実行
	 *
	 * @param <T> ジェネリックス
	 * @param cls 返却クラスタイプ
	 * @param query SQL
	 * @param vals パラメタ値
	 * @return クエリオブジェクト
	 */
	default <T> List<T> selectAll(Class<T> cls, String query, Object... vals) {
		final Map<String, Object> map = Stream.of(vals).collect(HashMap::new,
				(m, v) -> m.put(String.valueOf(m.size() + 1), v), HashMap::putAll);
		return selectAll(cls, query, map);
	}

	/**
	 * ネイティブSQLクエリ実行
	 *
	 * @param <T> ジェネリックス
	 * @param cls 返却クラスタイプ
	 * @param query SQL
	 * @param map パラメタ値マップ
	 * @return クエリオブジェクト
	 */
	<T> List<T> selectAll(Class<T> cls, String query, Map<String, ?> map);

	/**
	 * SQL更新実行
	 *
	 * @param query クエリ
	 * @param vals パラメタ値
	 * @return 処理件数
	 */
	default int execute(String query, Object... vals) {
		final Map<String, Object> map = Stream.of(vals).collect(HashMap::new,
				(m, v) -> m.put(String.valueOf(m.size() + 1), v), HashMap::putAll);
		return execute(query, map);
	}

	/**
	 * SQL更新実行
	 *
	 * @param query クエリ
	 * @param map パラメタ値マップ
	 * @return 処理件数
	 */
	int execute(String query, Map<String, ?> map);

	/**
	 * 連番取得
	 *
	 * @param name 連番名
	 * @return 連番
	 */
	long sequence(String name);

	/**
	 * コミット処理
	 */
	void commit();

	/**
	 * ロールバック処理
	 *
	 * @return 処理結果
	 */
	boolean rollback();

	/**
	 * フラッシュ処理
	 *
	 * @return フラッシュされた場合 true を返す。対象なしの場合 false を返す。
	 */
	boolean flush();

	/**
	 * セションクローズ
	 */
	@Override
	void close();

	/**
	 * JDBC実行処理
	 *
	 * @param work 実行クラス
	 */
	void doWork(JdbcWork work);

	/**
	 * 排他待ち設定
	 *
	 * @param val 排他待ちフラグ
	 */
	void setNoWait(boolean val);

	/**
	 * JDBC実行
	 *
	 * @author Tadashi Nakayama
	 */
	interface JdbcWork {
		/**
		 * 実行処理
		 *
		 * @param conn コネクション
		 * @throws SQLException SQL例外
		 */
		void execute(Connection conn) throws SQLException;
	}
}
