package jp.agentec.sinaburocast.service;

import java.util.List;
import java.util.Map;

import jp.agentec.sinaburocast.common.SinaburoConstant;
import jp.agentec.sinaburocast.common.util.SinaburoUtil;
import jp.agentec.sinaburocast.dto.PointRecoveryDto;
import jp.agentec.sinaburocast.entity.Gift;
import jp.agentec.sinaburocast.entity.GiftExchange;
import jp.agentec.sinaburocast.entity.Member;
import jp.agentec.sinaburocast.entity.PointGet;

import org.seasar.extension.jdbc.where.SimpleWhere;
import org.seasar.framework.beans.util.BeanMap;
import org.seasar.framework.container.annotation.tiger.Component;
import org.seasar.framework.container.annotation.tiger.InstanceType;

@Component(instance = InstanceType.SINGLETON)
public class PointGetService extends AbstractService<PointGet> {

	private static final String ID_SEQ_NAME = "point_get_id_seq";

	public MemberService memberService;

	public GiftExchangeService giftExchangeService;

	public PointPeriodService pointPeriodService;


	/**
	 * IDを発行して、登録する。
	 */
	public int insertPointGet(PointGet pointGet, String insId) {
		pointGet.pointGetId =
			getSeqNextVal(Integer.class, ID_SEQ_NAME);
		return super.insert(pointGet, insId);
	}



	public PointGet findById(Integer pointGetId) {
		return select().id(pointGetId).getSingleResult();
	}

	public PointGet findBymemberId(Integer memberId) {
		return select().where(new SimpleWhere().eq("memberId", memberId)).getSingleResult();
	}

	public List<PointGet> findBymemberIdList(Integer memberId) {
		return select().where(new SimpleWhere().eq("memberId", memberId)).getResultList();
	}

	public List<PointGet> findAllOrderById() {
		return select().orderBy("pointGetId asc").getResultList();
	}

	/**
	 * ・ポイント取得履歴更新 ポイント交換申請による、消費ポイント数を増加する。
	 * 更新するレコードは複数、対象カラムは「pointUseNum,validFlg」 ・ポイント交換申請の登録 ・会員情報テーブルの有効ポイント数更新
	 *
	 * @param gift
	 * @param member
	 * @return
	 */
	public void cutDownPointLogic(Gift gift, Member member) {
		// 有効なレコードを取得順に取得
		List<PointGet> pointGetList = select().where(new SimpleWhere()
		                                      .eq("memberId", member.memberId)
		                                      .eq("validFlg",SinaburoConstant.ValidFlg.VALID))
											  .orderBy("pointGetDate")
											  .getResultList();

		int requiredPoint = gift.requiredPoint;
		// 各レコードごとに使えるポイント
		int validPoint = 0;
		// 消費ポイント
		int usePoint = 0;
		// 残ポイント
		int restPoint = 0;

		// ■ポイント取得履歴の更新
		for (int i = 0; i < pointGetList.size(); i++) {
			PointGet pointGet = pointGetList.get(i);

			if (requiredPoint == 0) {
				// 会員情報テーブル残ポイントを取得する。ここから取得するレコードはpointUseNumが0であるはず。
				restPoint += pointGet.pointGetNum - pointGet.pointUseNum;
				continue;
			}

			// 各レコードの使えるポイント
			// validPointは0以上pointGet.pointGetNum以下
			validPoint = pointGet.pointGetNum - pointGet.pointUseNum;

			// 消費ポイントが0になるまで
			usePoint = Math.min(validPoint, requiredPoint);

			requiredPoint = requiredPoint - usePoint;

			pointGet.pointUseNum += usePoint;

			// レコードが消費完了していたら。INVALIDにする。
			if (pointGet.pointGetNum - pointGet.pointUseNum == 0) {
				pointGet.validFlg = SinaburoConstant.ValidFlg.INVALID;
			} else {
				restPoint = pointGet.pointGetNum - pointGet.pointUseNum;
			}

			// 対象レコードを更新
			update(pointGet, member.loginId);
		}

		// ■ポイント交換申請の登録
		GiftExchange giftExchange = new GiftExchange();
		giftExchange.memberId = member.memberId;
		giftExchange.giftId = gift.giftId;
		giftExchange.cnt = gift.requiredPoint / SinaburoConstant.ECHOBO_MAI_POINT.MAI1;
		giftExchange.applyDate = SinaburoUtil.getTimestamp();

		giftExchangeService.insertGiftExchange(giftExchange, member.loginId);

		// ■メンバー情報を更新する。
		member.pointNum = restPoint;
		memberService.update(member, member.loginId);
	}

	public Map<String, Integer> findPointByMemberId(Integer memberId,
			String startDay, String endDay) {
		String sql = "select  sum( case WHEN substr(point_get_date, 1, 6) <= ? then point_get_num - point_use_num else 0 end ) as point1,"
				+ "sum( case WHEN substr(point_get_date, 1, 6) between ? and ? then point_get_num - point_use_num else 0 end ) as point2 "
				+ "from t_point_get where member_id = ? and valid_flg = 1 ";

		@SuppressWarnings("unchecked")
		Map<String, Integer> cnt = jdbcManager.selectBySql(Map.class, sql,
				startDay,endDay, endDay, memberId).getSingleResult();
		return cnt;
	}

	public String findPointByMemberIdAdmin(Integer memberId) {
		String sql = "select sum(point_get_num - point_use_num) as point "
				+ "from t_point_get where member_id = ? and valid_flg = 1 ";

		@SuppressWarnings("unchecked")
		Map<String, Long> cnt = jdbcManager.selectBySql(Map.class, sql,
				memberId).getSingleResult();
		if (cnt.get("point") == null) {
			return "0";
		} else {
			return (String) cnt.get("point").toString();
		}
	}

	public Integer findPointSumByMemberId(Integer memberId) {
		String sql = "select sum(point_get_num - point_use_num) as point "
				+ "from t_point_get where member_id = ? and valid_flg = 1 ";

		Integer pointSum = jdbcManager.selectBySql(Integer.class, sql,memberId).getSingleResult();
		if (pointSum == null) {
			return 0;
		} else {
			return pointSum;
		}
	}

	/**
	 * ・ポイント取得履歴更新 ポイント交換申請による、消費ポイント数を増加する。
	 * 更新するレコードは複数、対象カラムは「pointUseNum,validFlg」 ・ポイント交換申請の登録 ・会員情報テーブルの有効ポイント数更新
	 *
	 * @param gift
	 * @param member
	 * @return
	 */
	public void upDownPointLogic(Integer point, Integer memberId, String adminId) {
		// 有効なレコードを取得順に取得
		List<PointGet> pointGetList = select().where(new SimpleWhere()
		                                      .eq("memberId", memberId)
		                                      .eq("validFlg",SinaburoConstant.ValidFlg.VALID))
											  .orderBy("pointGetDate")
											  .getResultList();

		int requiredPoint = point;
		// 各レコードごとに使えるポイント
		int validPoint = 0;
		// 消費ポイント
		int usePoint = 0;

		// ■ポイント取得履歴の更新
		for (int i = 0; i < pointGetList.size(); i++) {
			PointGet pointGet = pointGetList.get(i);

			// 各レコードの使えるポイント
			// validPointは0以上pointGet.pointGetNum以下
			validPoint = pointGet.pointGetNum - pointGet.pointUseNum;

			// 消費ポイントが0になるまで
			usePoint = Math.min(validPoint, requiredPoint);

			requiredPoint = requiredPoint - usePoint;

			pointGet.pointUseNum += usePoint;

			// レコードが消費完了していたら。INVALIDにする。
			if (pointGet.pointGetNum - pointGet.pointUseNum == 0) {
				pointGet.validFlg = SinaburoConstant.ValidFlg.INVALID;
			}

			// 対象レコードを更新
			update(pointGet, adminId);
		}

	}

	/**
	 * ポイント有効期限変更時、ポイント取得日を変更処理した日とする
	 * @param pointPeriod ポイント有効期
	 * @return ret 更新件数
	 */
	public int updatePointGetDateByPointPeriod(String pointPeriod,String loginId) {

		String sql = "update t_point_get "
				+ "set "
				+ "point_get_date = to_char(current_timestamp,'yyyymmdd'), "
				+ "update_date = now(), "
				+ "update_id = '" + loginId + "' "
				+ "where point_get_id in "
				+ "( "
				+ "select point_get_id "
				+ "from "
				+ "t_point_get "
				+ "where "
				+ "to_timestamp(point_get_date,'yyyymmdd') + '-1 days' + ' " + pointPeriod + " years' < date_trunc('day', current_timestamp) "
				+ "and valid_flg = 1 "
				+ ") ";

		int ret = jdbcManager.updateBySql(sql).execute();
		return ret;
    }

//	/**
//	 * ポイント有効期間切れ処理
//	 * 1.ポイント取得情報で有効期限が切れたレコードをvalid_flgを0に変更。
//	 * 2.会員情報のポイントを更新する。
//	 * @param strYearsAgoOneMonthAfter X年前の1ヶ月後
//	 */
//	public void cutExpiredPoint(String strYearsAgoOneMonthAfter) {
//		//ポイント取得情報テーブル更新
//		//有効期限が切れたレコードをvalid_flg=0に変更
//		String sql = "UPDATE t_point_get SET valid_flg = '0' where point_get_date < ?";
//		jdbcManager.updateBySql(sql,String.class).params(strYearsAgoOneMonthAfter+"01").execute();
//
//		//会員情報のポイントを更新する。既存ポイントが0以下は更新対象外(※パフォーマンスのため)
//		StringBuffer sqlSb = new StringBuffer();
//		sqlSb.append("UPDATE m_member M SET point_num = COALESCE(P.validPoint,0) FROM  ");
//		sqlSb.append("       (select MT.member_id,validPoint from m_member MT ");
//		sqlSb.append("       LEFT JOIN (select SUM(point_get_num - point_use_num) validPoint,member_id from t_point_get WHERE  ");
//		sqlSb.append("           valid_flg = 1  ");
//		sqlSb.append("           GROUP BY member_id) PT ");
//		sqlSb.append("           ON MT.member_id = PT.member_id  ");
//		sqlSb.append("       ) P ");
//		sqlSb.append("WHERE     M.member_id = P.member_id ");
//		sqlSb.append("      AND M.point_Num > ? ");
//		jdbcManager.updateBySql(sqlSb.toString(),Integer.class).params(0).execute();
//	}
	
	/**
	 * ポイント有効期間切れ処理
	 * 1.ポイント取得情報で有効期限が切れたレコードをvalid_flgを0に変更。
	 * 2.会員情報のポイントを更新する。
	 * @param strYearsAgoOneMonthAfter X年前の1ヶ月後
	 */
	public void cutExpiredPointBySql(String strYearsAgoOneMonthAfter) {
		//ポイント取得情報テーブル更新
		//有効期限が切れたレコードをvalid_flg=0に変更
		String sql = "UPDATE t_point_get SET valid_flg = '0' where point_get_date < ?";
		jdbcManager.updateBySql(sql,String.class).params(strYearsAgoOneMonthAfter+"01").execute();
		//会員情報のポイントを更新する。既存ポイントが0以下は更新対象外(※パフォーマンスのため)
		jdbcManager.updateBySqlFile("jp/agentec/sinaburocast/service/cm/cutExpiredPointBySql.sql",0).execute();
	}

	
	
	/**
	 * ・ポイント取得履歴更新 ポイント交換申請による、消費ポイント数を増加する。
	 * 更新するレコードは複数、対象カラムは「pointUseNum,validFlg」 ・ポイント交換申請の登録 ・会員情報テーブルの有効ポイント数更新
	 * マイレーション用
	 *
	 * @param userPoint 減らすポイント
	 * @param member
	 * @return restPoint POINTテーブルに残っているポイント
	 */
	public int cutDownPointLogicForMaigration(int userPoint, Integer memberId,String loginId) {
		// 有効なレコードを取得順に取得
		List<PointGet> pointGetList = select().where(new SimpleWhere()
		                                      .eq("memberId", memberId)
		                                      .eq("validFlg",SinaburoConstant.ValidFlg.VALID))
		                                      .orderBy("pointGetDate, pointGetid")
											  .getResultList();

		int requiredPoint = userPoint;
		// 各レコードごとに使えるポイント
		int validPoint = 0;
		// 消費ポイント
		int usePoint = 0;
		// 残ポイント
		int restPoint = 0;

		// ■ポイント取得履歴の更新
		for (int i = 0; i < pointGetList.size(); i++) {
			PointGet pointGet = pointGetList.get(i);

			if (requiredPoint == 0) {
				// 会員情報テーブル残ポイントを取得する。ここから取得するレコードはpointUseNumが0であるはず。
				restPoint += pointGet.pointGetNum - pointGet.pointUseNum;
				continue;
			}

			// 各レコードの使えるポイント
			// validPointは0以上pointGet.pointGetNum以下
			validPoint = pointGet.pointGetNum - pointGet.pointUseNum;

			// 消費ポイントが0になるまで
			usePoint = Math.min(validPoint, requiredPoint);

			requiredPoint = requiredPoint - usePoint;

			pointGet.pointUseNum += usePoint;

			// レコードが消費完了していたら。INVALIDにする。
			if (pointGet.pointGetNum - pointGet.pointUseNum == 0) {
				pointGet.validFlg = SinaburoConstant.ValidFlg.INVALID;
			} else {
				restPoint = pointGet.pointGetNum - pointGet.pointUseNum;
			}

			// 対象レコードを更新
			update(pointGet, loginId);
		}
		
		return restPoint;
	}

	public void clearPointUseNumByMemberId(Integer memberId) {
		String sql = "UPDATE t_point_get SET point_use_num = 0, valid_flg = '1' where member_Id = ?";
		jdbcManager.updateBySql(sql, Integer.class).params(memberId).execute();
	}

	/**
	 * マイレーション用
	 * MEMBERテーブルPOINT_GETテーブルとPOINTの差異があるレコードを取得
	 * MEMBER_Id,POINT_NUM,ppoint,mp(MならMEMBERが大きい、Pならポイントが大きい)
	 * @return
	 */
	public BeanMap getDefaultPoint(Integer memberId) {
		StringBuilder sql = new StringBuilder();
		sql.append("select *,(CASE WHEN MP ='P' THEN ppoint-POINT_NUM ELSE 0 END) herasuPoint from ( ");
		sql.append("	select m.MEMBER_ID ,M.POINT_NUM ,P.zan ppoint, (CASE WHEN M.POINT_NUM > P.ZAN THEN 'M' WHEN M.POINT_NUM < P.ZAN THEN 'P' END) MP from m_member M  ");
		sql.append("	INNER JOIN  ");
		sql.append("	(select member_id,sum(point_get_num-point_use_num) ZAN from T_POINT_GET where VALID_FLG=1  and member_id =? group by member_id) P  ");
		sql.append("	ON	M.MEMBER_ID = P.MEMBER_ID  ");
		sql.append("	AND	M.POINT_NUM != P.ZAN  ");
		sql.append("	where m.member_Id = ?) R");
		
		return jdbcManager.selectBySql(BeanMap.class,sql.toString(),memberId,memberId).getSingleResult();
	}
	
	/**
	 * 
	 * @return
	 */
	public List<BeanMap> memberPointLarge() {
		StringBuilder sql = new StringBuilder();
		sql.append("select m.MEMBER_ID ,P.zan ppoint ,M.POINT_NUM,''''||M.POINT_NUM,P.ZAN ||'/'||M.POINT_NUM MP from m_member M   ");
		sql.append("	INNER JOIN   ");
		sql.append("	(select member_id,sum(point_get_num-point_use_num) ZAN from T_POINT_GET where VALID_FLG=1  and INSERT_ID='BATCH' group by member_id) P   ");
		sql.append("	ON	M.MEMBER_ID = P.MEMBER_ID   ");
		sql.append("	AND	M.POINT_NUM > P.ZAN   ");
		sql.append("	  ");
		sql.append("	order by m.member_id,mp ");
		
		return jdbcManager.selectBySql(BeanMap.class,sql.toString()).getResultList();
	}

	public List<PointRecoveryDto> findPointRecoveryDto(){
		StringBuilder sql = new StringBuilder();
		sql.append("select member_id, sum(point_get_num) as amount_point_get_num, sum(point_use_num) as amount_point_use_num ");
		sql.append("from t_point_get ");
		sql.append("group by member_id ");
		sql.append("having sum(point_get_num) <> sum(point_use_num) ");
		sql.append("and sum(point_use_num) > 0 ");

		return jdbcManager.selectBySql(PointRecoveryDto.class, sql.toString()).getResultList();
	}
}