package com.bizzan.bitrade.dao;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import com.bizzan.bitrade.dao.base.BaseDao;
import com.bizzan.bitrade.entity.MemberContractPosition;
import com.bizzan.bitrade.vo.PositionStatVo;

public interface MemberContractPositionDao extends BaseDao<MemberContractPosition> {

	@Transactional
    @Modifying
    @Query("update MemberContractPosition set frozenPosition=frozenPosition + :amount where id = :id and total_position-closed_position>=:amount")
    int freezePosition(@Param("id") long id, @Param("amount") BigDecimal amount);
	
	@Transactional
    @Modifying
    @Query("update MemberContractPosition set frozenPosition=frozenPosition - :amount where id = :id")
    int thawPosition(@Param("id") long id, @Param("amount") BigDecimal amount);
	
	@Transactional
    @Modifying
    @Query("update MemberContractPosition set leverage=:leverage, principalAmount=principalAmount - :marginDifference where id = :id")
    int modifyLeverage(@Param("id") long id, @Param("leverage") BigDecimal leverage, @Param("marginDifference") BigDecimal marginDifference);
	
	List<MemberContractPosition> findByMemberIdAndStatus(Long memberId, Integer status);
	
	@Query(value = "select * from member_contract_position where member_id=:memberId and contract_id=:contractId and direction=:direction and money_type=:moneyType and status<>2 and type=0 and follow_position_id is null",nativeQuery = true)
	MemberContractPosition findCurrentIntegralPosition(@Param("memberId") Long memberId, @Param("contractId") Long contractId, @Param("direction") Integer direction, @Param("moneyType") Integer moneyType);
	
	@Query(value = "select * from member_contract_position where member_id=:memberId and contract_id=:contractId and direction=:direction and money_type=:moneyType and status<>2 and type=0 and follow_position_id is not null",nativeQuery = true)
	MemberContractPosition findCurrentIntegralFollowPosition(@Param("memberId") Long memberId, @Param("contractId") Long contractId, @Param("direction") Integer direction, @Param("moneyType") Integer moneyType);
	
	@Query(value = "select id from member_contract_position where member_id=:memberId and contract_id=:contractId and direction=:direction and money_type=:moneyType and status<>2 and type=0 and follow_position_id is null",nativeQuery = true)
	Long getCurrentIntegralPositionId(@Param("memberId") Long memberId, @Param("contractId") Long contractId, @Param("direction") Integer direction, @Param("moneyType") Integer moneyType);
	
	@Query(value = "select id from member_contract_position where member_id=:memberId and follow_position_id=:followPositionId and status<>2",nativeQuery = true)
	Long getPositionIdByMemberIdAndFollowPositionId(@Param("memberId") Long memberId, @Param("followPositionId") Long followPositionId);
	
	@Query(value = "select sum(total_position-closed_position) from member_contract_position where member_id=:memberId and status<>2",nativeQuery = true)
	BigDecimal sumCurrentPositionByMemberId(@Param("memberId") long memberId);
	
	@Transactional(rollbackFor = Exception.class)
    @Modifying
    @Query(value = "UPDATE member_contract_position SET is_lock=:isLock WHERE id=:id",nativeQuery = true)
    void updateIsLock(@Param("id")Long id, @Param("isLock")int isLock);
	
	@Query(value="select sum(a.total_position-a.closed_position) position from member_contract_position a where a.contract_id=:contractId and direction=:direction and a.status<>2 and a.experience_record_id is null",nativeQuery = true)
	BigDecimal sumPositionsByCoinAndDirection(@Param("contractId")Long contractId, @Param("direction")int direction);

	@Query(value="select sum(a.total_position-a.closed_position) position from member_contract_position a where a.contract_id=:contractId and direction=:direction and a.status<>2 and a.follow_position_id is null and a.experience_record_id is null",nativeQuery = true)
	BigDecimal sumSelfPositionsByCoinAndDirection(@Param("contractId")Long contractId, @Param("direction")int direction);

	@Query(value="select sum(a.total_position-a.closed_position) position from member_contract_position a where a.contract_id=:contractId and direction=:direction and a.status<>2 and a.follow_position_id is not null and a.experience_record_id is null",nativeQuery = true)
	BigDecimal sumCopyPositionsByCoinAndDirection(@Param("contractId")Long contractId, @Param("direction")int direction);
	
	@Query(value="select a.id from member_contract_position a where a.member_id=:memberId and a.contract_id=:contractId and a.status<>2 and a.follow_position_id is null",nativeQuery = true)
	List<BigInteger> findIdListByMemberIdAndContractId(@Param("memberId")Long memberId, @Param("contractId")Long contractId);
	
	@Query(value="select a.id from member_contract_position a where a.member_id=:memberId and a.status<>2 and a.follow_position_id is null",nativeQuery = true)
	List<BigInteger> findIdListByMemberId(@Param("memberId")Long memberId);
	
	@Query(value="SELECT member_id from member_contract_position where create_time > :startTime group by member_id having count(member_id) > 10 and sum(violate_time)>0",nativeQuery = true)
	List<BigInteger> findSupervisedMemberList(@Param("startTime")String startTime);
	
	@Query(value="SELECT count(member_id) as position_count, sum(violate_time) as violate_count from member_contract_position where member_id=:memberId and create_time between :startTime and :endTime",nativeQuery = true)
	Object[][] statSupervised(@Param("memberId")Long memberId,@Param("startTime")String startTime, @Param("endTime")String endTime);
	
	@Query(value="select member_id, count(member_id) as total_count, sum(TIMESTAMPDIFF(MINUTE, open_time, close_time)<3) AS short_duration_count from member_contract_position where follow_position_id is null and UNIX_TIMESTAMP(create_time) between :startTime and :endTime group by member_id having total_count >= 10 and short_duration_count / total_count > 0.3",nativeQuery = true)
	Object[][] statFarming(@Param("startTime")long startTime, @Param("endTime")long endTime);

	/**
	 * 补仓原子写入：delta 字段用 field+delta，加权均价/时间直接覆盖。
	 * 仅用于已有仓位的加仓（totalPosition > 0），新建仓位仍走 save()。
	 */
	@Transactional(rollbackFor = Exception.class)
	@Modifying
	@Query(value =
		"UPDATE member_contract_position SET " +
		"  total_position         = total_position         + :volume, " +
		"  principal_amount       = principal_amount       + :principalAmount, " +
		"  total_principal_amount = total_principal_amount + :principalAmount, " +
		"  commission_fee         = commission_fee         + :openFee, " +
		"  open_price             = :openPrice, " +
		"  open_time              = :openTime, " +
		"  latest_open_time       = :latestOpenTime, " +
		"  leverage               = :leverage, " +
		"  open_type              = :openType, " +
		"  share_number           = :shareNumber " +
		"WHERE id = :id",
		nativeQuery = true)
	int applyOpenIncrement(
		@Param("id")              long id,
		@Param("volume")          BigDecimal volume,
		@Param("principalAmount") BigDecimal principalAmount,
		@Param("openFee")         BigDecimal openFee,
		@Param("openPrice")       BigDecimal openPrice,
		@Param("openTime")        Date openTime,
		@Param("latestOpenTime")  Date latestOpenTime,
		@Param("leverage")        BigDecimal leverage,
		@Param("openType")        int openType,
		@Param("shareNumber")     BigDecimal shareNumber
	);

	/**
	 * 平仓原子写入：delta 字段用 field+delta，加权均价字段直接覆盖，避免 JPA 全字段覆盖式 save 引发的并发丢失更新。
	 * violateTime=0 表示无违规，=1 表示违规（仅 processCloseOrder 可能传 1）。
	 * WHERE 条件保证：closed_position + volume <= total_position 且 frozen_position >= volume。
	 * 若条件不满足（并发重复平仓等异常情况）UPDATE 影响行数为 0，调用方应检查返回值并作失败处理。
	 */
	@Transactional(rollbackFor = Exception.class)
	@Modifying
	@Query(value =
		"UPDATE member_contract_position SET " +
		"  closed_position   = closed_position   + :volume, " +
		"  frozen_position   = frozen_position   - :volume, " +
		"  commission_fee    = commission_fee    + :commissionFee, " +
		"  usdt_profit       = usdt_profit       + :profit, " +
		"  principal_amount  = :principalAmount, " +
		"  closed_open_price = :closedOpenPrice, " +
		"  close_price       = :closePrice, " +
		"  status            = :status, " +
		"  close_type        = :closeType, " +
		"  close_time        = :closeTime, " +
		"  current_price     = :currentPrice, " +
		"  violate_time      = :violateTime " +
		"WHERE id = :id " +
		"  AND closed_position + :volume <= total_position " +
		"  AND frozen_position >= :volume",
		nativeQuery = true)
	int applyCloseResult(
		@Param("id")              long id,
		@Param("volume")          BigDecimal volume,
		@Param("commissionFee")   BigDecimal commissionFee,
		@Param("profit")          BigDecimal profit,
		@Param("principalAmount") BigDecimal principalAmount,
		@Param("closedOpenPrice") BigDecimal closedOpenPrice,
		@Param("closePrice")      BigDecimal closePrice,
		@Param("status")          int status,
		@Param("closeType")       int closeType,
		@Param("closeTime")       Date closeTime,
		@Param("currentPrice")    BigDecimal currentPrice,
		@Param("violateTime")     int violateTime
	);

	/**
	 * 体验金仓位直接关闭：跳过钱包操作，更新仓位状态及盈亏字段。
	 * 只对 experience_record_id IS NOT NULL 且 status=0(STARTED) 的仓位生效。
	 */
	@Transactional(rollbackFor = Exception.class)
	@Modifying
	@Query(value =
		"UPDATE member_contract_position SET " +
		"  status            = 2, " +
		"  close_type        = :closeType, " +
		"  close_price       = :closePrice, " +
		"  current_price     = :closePrice, " +
		"  close_time        = :closeTime, " +
		"  closed_position   = total_position, " +
		"  frozen_position   = 0, " +
		"  principal_amount  = 0, " +
		"  commission_fee    = commission_fee + :closeFee, " +
		"  usdt_profit       = usdt_profit + :profit, " +
		"  closed_open_price = :closedOpenPrice " +
		"WHERE id = :id " +
		"  AND status = 0 " +
		"  AND experience_record_id IS NOT NULL",
		nativeQuery = true)
	int closeExperienceGiftPosition(
		@Param("id")              long id,
		@Param("closePrice")      BigDecimal closePrice,
		@Param("closeType")       int closeType,
		@Param("closeTime")       Date closeTime,
		@Param("profit")          BigDecimal profit,
		@Param("closedOpenPrice") BigDecimal closedOpenPrice,
		@Param("closeFee")        BigDecimal closeFee
	);
}
