/**
* @Author: zou.yingbin
* @Create  : 2021/1/18 9:26
* @Modify  : 2021/1/18 9:26
 */

package models

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"mtp2_if/db"
	"mtp2_if/mtpcache"
	"mtp2_if/pb"
	"mtp2_if/rediscli"
)

//实时敞口
type ErmcpRealExposureModel struct {
	MiddleGoodsID      uint32  // 套保品种
	AreaUserID         uint32  // 所属机构
	OriBuyPlanQty      float64 // 期初采购计划数量
	OriBuyPricedQty    float64 // 期初采购合同已定价数量
	OriSellPlanQty     float64 // 期初销售计划数量
	OriSellPricedQty   float64 // 期初销售合同已定价数量
	OriBuyFutureQty    uint64  // 期初买入期货数量
	OriSellFutureQty   uint64  // 期初卖出期货数量
	BuyPlanQty         float64 // 采购计划数量
	BuyPricedQty       float64 // 采购合同已定价数量
	SellPlanQty        float64 // 销售计划数量
	SellPricedQty      float64 // 销售合同已定价数量
	BuyFutureQty       float64 // 买入期货数量
	SellFutureQty      float64 // 卖出期货数量
	TotalSpotQty       float64 // 现货数量
	TotalFutureQty     float64 // 期货数量
	TotalExposure      float64 // 总敞口
	TotalHedgeRatio    float64 // 敞口比例
	TotalNeedHedgeQty  float64 // 期货应套保量
	NeedHedgeExposoure float64 // 应套保敞口
	NeedHedgeRatio     float64 // 应套保敞口比例
	//************以下需计算或非redis数据************//
	MiddleGoodsName       string  // 套保品种名称
	MiddleGoodsCode       string  // 套保品种代码
	MiddleGoodsHedgeRatio float64 // 应套保比例
	OriTotalSpotQty       float64 // 期初现货数量=(期初销售计划数量-期初销售合同已定价数量)-(期初采购计划数量-期初采购合同已定价数量)
	OriTotalFutuQty       uint64  // 期初期货数量=期初买入期货数量-期初卖出期货数量
}

// 计算相关字段
func (r *ErmcpRealExposureModel) calc() {
	r.OriTotalSpotQty = (r.OriSellPlanQty - r.OriSellPricedQty) - (r.OriBuyPlanQty - r.OriBuyPricedQty)
	r.OriTotalFutuQty = r.OriBuyFutureQty - r.OriSellFutureQty
}

func (r *ErmcpRealExposureModel) ParseFromProto(v *pb.ErmcpAreaExposure) {
	r.MiddleGoodsID = *v.MiddleGoodsID
	r.AreaUserID = *v.AreaUserID
	r.OriBuyPlanQty = *v.OriBuyPlanQty
	r.OriBuyPricedQty = *v.OriBuyPricedQty
	r.OriSellPlanQty = *v.OriSellPlanQty
	r.OriSellPricedQty = *v.OriSellPricedQty
	r.OriBuyFutureQty = *v.OriBuyFutureQty
	r.OriSellFutureQty = *v.OriSellFutureQty
	r.BuyPlanQty = *v.BuyPlanQty
	r.BuyPricedQty = *v.BuyPricedQty
	r.SellPlanQty = *v.SellPlanQty
	r.SellPricedQty = *v.SellPricedQty
	r.BuyFutureQty = *v.BuyFutureQty
	r.SellFutureQty = *v.SellFutureQty
	r.TotalSpotQty = *v.TotalSpotQty
	r.TotalFutureQty = *v.TotalFutureQty
	r.TotalExposure = *v.TotalExposure
	r.TotalHedgeRatio = *v.TotalHedgeRatio
	r.TotalNeedHedgeQty = *v.TotalNeedHedgeQty
	r.NeedHedgeExposoure = *v.NeedHedgeExposoure
	r.NeedHedgeRatio = *v.NeedHedgeRatio
	// 执行相关计算
	r.calc()
}

// 实时敞口数据: Redis数据 + 套保品种信息表
func (r *ErmcpRealExposureModel) GetData() ([]ErmcpRealExposureModel, error) {
	// 获取关联的套路商品
	if sGoods, err := mtpcache.GetMiddleGoodsByUserID(r.AreaUserID); err == nil {
		sData := make([]ErmcpRealExposureModel, 0)
		// 从Redis获取数据
		for i := range sGoods {
			key := fmt.Sprintf("ErmcpAreaExposure:%d_%d", sGoods[i].AREAUSERID, sGoods[i].MIDDLEGOODSID)
			if ret, err := rediscli.GetRedisClient().Get(key).Result(); err == nil {
				if len(ret) > 0 {
					var data pb.ErmcpAreaExposure
					if err := proto.Unmarshal([]byte(ret), &data); err == nil {
						var m ErmcpRealExposureModel
						m.ParseFromProto(&data)
						sData = append(sData, m)
					}
				}
			}
		}

		return sData, nil
	}

	return nil, nil
}

/*************敞口明细**************/

//敞口明细结构
type ErmcpExposureDetailModel struct {
	Createtime      string  `json:"createtime"  xorm:"'createtime'"`           // 时间
	Areauserid      uint32  `json:"areauserid"  xorm:"'areauserid'"`           // 机构ID
	Logtype         int32   `json:"logtype"  xorm:"'logtype'"`                 // 类型 - 1:套保计划 2:现货合同
	Contracttype    int32   `json:"contracttype"  xorm:"'contracttype'"`       // 现货合同类型 - 1:采购 -1:销售
	Wrstandardid    int32   `json:"wrstandardid"  xorm:"'wrstandardid'"`       // 现货商品ID
	Qty             float64 `json:"qty"  xorm:"'qty'"`                         // 数量
	RelateNo        string  `json:"relateNo"  xorm:"'relateNo'"`               // 现货合同/套保计划编号
	Middlegoodsname string  `json:"middlegoodsname"  xorm:"'middlegoodsname'"` // 套保商品名称
	Middlegoodscode string  `json:"middlegoodscode"  xorm:"'middlegoodscode'"` // 套保商品代码
	MiddlegoodsId   int32   `json:"middlegoodsId"  xorm:"'middlegoodsId'"`     // 套保商品id
	Unitid          int32   `json:"-"  xorm:"'unitid'"`                        // 现货商品单位ID
	Wrstandardname  string  `json:"wrstandardname"  xorm:"'wrstandardname'"`   // 现货商品名称
	Wrstandardcode  string  `json:"wrstandardcode"  xorm:"'wrstandardcode'"`   // 现货商品代码
	Enumdicname     string  `json:"enumdicname"  xorm:"'enumdicname'"`         // 现货商品单位名称
	ChangeQty       float64 `json:"changeQty"  xorm:"'changeQty'"`             // 套保变动量
	Convertfactor   float64 `json:"convertfactor"  xorm:"'convertfactor'"`     // 标仓系数
	Convertratio    float64 `json:"convertratio"  xorm:"'convertratio'"`       // 套保系数
}

func (r *ErmcpExposureDetailModel) buildSql() string {
	str := "with tmp as" +
		" (select 2                as LogType," +
		"         s.spotcontractid as relatedid," +
		"         s.contractno     as relateNo" +
		"    from ermcp_spotcontract s" +
		"  union all" +
		"  select 1, t.hedgeplanid as relateid, t.hedgeplanno as relateNo" +
		"    from ermcp_hedgeplan t)" +
		"select to_char(t.createtime, 'yyyy-mm-dd hh24:mi:ss') createtime," +
		"       t.middlegoodsid," +
		"       t.areauserid," +
		"       t.logtype," +
		"       t.contracttype," +
		"       wc.wrstandardid," +
		"       t.qty," +
		"       t.convertfactor," +
		"       t.convertratio," +
		"       t.qty * t.convertfactor * t.convertratio as changeQty," +
		"       tmp.relateNo," +
		"       m.middlegoodsname," +
		"       m.middlegoodscode," +
		"       w.unitid," +
		"       w.wrstandardname," +
		"       w.wrstandardcode," +
		"       e.enumdicname" +
		"  from ermcp_spotexposurelog t" +
		"  left join erms2_wrsconvertdetail wc" +
		"    on t.wrstandardid = wc.wrstandardid" +
		"   and t.middlegoodsid = wc.middlegoodsid" +
		"  left join erms_middlegoods m" +
		"    on t.middlegoodsid = m.middlegoodsid" +
		"  left join wrstandard w" +
		"    on t.wrstandardid = w.wrstandardid" +
		"  left join enumdicitem e" +
		"    on w.unitid = e.enumitemname" +
		"   and e.enumdiccode = 'goodsunit'" +
		"  left join tmp" +
		"    on t.logtype = tmp.LogType" +
		"   and t.relatedid = tmp.relatedid" +
		"  where t.middlegoodsid=%v and t.areauserid=%v"

	return fmt.Sprintf(str, r.MiddlegoodsId, r.Areauserid)
}

// 查询敞口现货明细
func (r *ErmcpExposureDetailModel) GetData() ([]ErmcpExposureDetailModel, error) {
	e := db.GetEngine()
	s := e.SQL(r.buildSql())
	sData := make([]ErmcpExposureDetailModel, 0)
	if err := s.Find(&sData); err != nil {
		return nil, err
	}
	return sData, nil
}

/// 现货头寸

// 现货头寸数据
type AreaSpotModel struct {
	WRSTANDARDID     int32   `json:"wrstandardid"  xorm:"'WRSTANDARDID'"`     // 现货商品ID
	WRSTANDARDNAME   string  `json:"wrstandardname"  xorm:"'WRSTANDARDNAME'"` // 现货品种
	WRSTANDARDCODE   string  `json:"wrstandardcode"  xorm:"'WRSTANDARDCODE'"` // 现货品种代码
	AREAUSERID       int32   `json:"areauserid"  xorm:"'AREAUSERID'"`         // 所属机构
	ORIBUYPLANQTY    float64 `json:"-"  xorm:"'ORIBUYPLANQTY'"`               // 期初采购计划数量
	ORIBUYPRICEDQTY  float64 `json:"-"  xorm:"'ORIBUYPRICEDQTY'"`             // 期初采购合同已定价数量
	ORISELLPLANQTY   float64 `json:"-"  xorm:"'ORISELLPLANQTY'"`              // 期初销售计划数量
	ORISELLPRICEDQTY float64 `json:"-"  xorm:"'ORISELLPRICEDQTY'"`            // 期初销售合同已定价数量
	BUYPLANQTY       float64 `json:"-"  xorm:"'BUYPLANQTY'"`                  // 采购计划数量
	BUYPRICEDQTY     float64 `json:"-"  xorm:"'BUYPRICEDQTY'"`                // 采购合同已定价数量
	SELLPLANQTY      float64 `json:"-"  xorm:"'SELLPLANQTY'"`                 // 销售计划数量
	SELLPRICEDQTY    float64 `json:"-"  xorm:"'SELLPRICEDQTY'"`               // 销售合同已定价数量
	TOTALSPOTQTY     float64 `json:"totalspotqty"  xorm:"'TOTALSPOTQTY'"`     // 当前数量(现货头寸总量) =  (销售计划数量 - 销售已定价数量)  - (采购计划数量 - 采购已定价数量)
	OriToalSpotQty   float64 `json:"oritoalspotqty"  xorm:"'OriToalSpotQty'"` // 昨日数量
	IncreaseQty      float64 `json:"increaseqty"  xorm:"'IncreaseQty'"`       // 增加数量=销售计划数量+采购已定价数量
	DecreaseQty      float64 `json:"decreaseqty"  xorm:"'DecreaseQty'"`       // 减少数量=-(销售已定价数量+采购计划数量)
	UPDATETIME       string  `json:"updatetime"  xorm:"'UPDATETIME'"`         // 更新时间
}

// 进行相关字段的值计算
func (r *AreaSpotModel) calc() {
	r.IncreaseQty = r.SELLPLANQTY + r.BUYPRICEDQTY
	r.DecreaseQty = (r.SELLPRICEDQTY + r.BUYPLANQTY) * -1
	r.OriToalSpotQty = (r.ORISELLPLANQTY - r.ORISELLPRICEDQTY) - (r.ORIBUYPLANQTY - r.ORIBUYPRICEDQTY)
}

func (r *AreaSpotModel) buildSql() string {
	str := "select t.WRSTANDARDID," +
		"       w.WRSTANDARDNAME," +
		"       w.WRSTANDARDCODE," +
		"       t.AREAUSERID," +
		"       t.ORIBUYPLANQTY," +
		"       t.ORIBUYPRICEDQTY," +
		"       t.ORISELLPLANQTY," +
		"       t.ORISELLPRICEDQTY," +
		"       t.BUYPLANQTY," +
		"       t.BUYPRICEDQTY," +
		"       t.SELLPLANQTY," +
		"       t.SELLPRICEDQTY," +
		"       t.TOTALSPOTQTY," +
		"       to_char(t.UPDATETIME,'yyyy-mm-dd hh24:mi:ss') UPDATETIME" +
		"  from ermcp_areaspot t" +
		"  left join wrstandard w" +
		"    on t.wrstandardid = w.wrstandardid" +
		"  where t.areauserid=%v"
	return fmt.Sprintf(str, r.AREAUSERID)
}

// 从数据库中查询现货头寸
func (r *AreaSpotModel) GetData() ([]AreaSpotModel, error) {
	e := db.GetEngine()
	sData := make([]AreaSpotModel, 0)
	if err := e.SQL(r.buildSql()).Find(&sData); err != nil {
		return nil, err
	}
	for i := range sData {
		sData[i].calc()
	}
	return sData, nil
}

// 现货头寸-现货明细
type ErmcpAreaSpotDetailModel struct {
	Relatedid      string  `json:"relatedid"  xorm:"'relatedid'"`           // 套保计划ID/现货合同ID
	Relatedno      string  `json:"relatedno"  xorm:"'relatedno'"`           // 编号
	LogType        int32   `json:"logtype"  xorm:"'logType'"`               // 记录类型 1-套保 2-现货合同
	Contracttype   int32   `json:"contracttype"  xorm:"'contracttype'"`     // 合同类型 1-采购 -1-销售
	Wrstandardname string  `json:"wrstandardname"  xorm:"'wrstandardname'"` // 现货商品名称
	Wrstandardcode string  `json:"wrstandardcode"  xorm:"'wrstandardcode'"` // 现货商品代码
	Qty            float64 `json:"qty"  xorm:"'qty'"`                       // 数量
	Strtime        string  `json:"strtime"  xorm:"'strtime'"`               // 时间
	Enumdicname    string  `json:"enumdicname"`                             // 现货商品单位名称
	Recordname     string  `json:"recordname"`                              // 类型名称
	Unitid         int32   `json:"-"  xorm:"'UNITID'"`                      // 单位ID
	UserId         int     `json:"-"`                                       // 所属用户ID
	WrstandardId   int32   `json:"-"`                                       // 现货商品ID
}

// 组建查询SQL
func (r *ErmcpAreaSpotDetailModel) buildSql() string {
	str := "select to_char(t.hedgeplanid) relatedid," +
		"       t.hedgeplanno relatedno," +
		"       1 as logType," +
		"       t.contracttype," +
		"       w.wrstandardname," +
		"       w.wrstandardcode," +
		"       t.planqty qty," +
		"        w.unitid," +
		"       to_char(t.createtime, 'yyyy-mm-dd hh:mi:ss') strtime" +
		"  from ermcp_hedgeplan t" +
		"  left join wrstandard w" +
		"    on t.wrstandardid = w.wrstandardid" +
		" where t.hedgeplanstatus in (2, 3, 5)" +
		"   and t.areauserid = %v and t.wrstandardid = %v" +
		" union all " +
		"select to_char(t.spotcontractid)," +
		"       t.contractno," +
		"       1 as logType," +
		"       t.contracttype," +
		"       w.wrstandardname," +
		"       w.wrstandardcode," +
		"       t.qty," +
		"       w.unitid," +
		"       to_char(t.createtime, 'yyyy-mm-dd hh:mi:ss') strtime" +
		"  from ermcp_spotcontract t" +
		"  left join wrstandard w" +
		"    on t.wrstandardid = w.wrstandardid" +
		" where t.contractstatus in (2, 3, 5)" +
		"   and t.userid = %v and t.wrstandardid = %v"

	return fmt.Sprintf(str, r.UserId, r.WrstandardId, r.UserId, r.WrstandardId)
}

// 现货头寸-明细:数据加工处理
func (r *ErmcpAreaSpotDetailModel) Calc() {
	var logTypeName, contractTypeName string
	if r.LogType == 1 {
		logTypeName = "计划"
	} else {
		logTypeName = "合同"
	}

	if r.Contracttype == 1 {
		contractTypeName = "采购"
	} else {
		contractTypeName = "销售"
	}

	r.Recordname = contractTypeName + logTypeName
	r.Enumdicname = mtpcache.GetEnumDicitemName(r.Unitid)
}

// 现货头寸-明细:从数据库中查询敞口现货头寸明细数据
func (r *ErmcpAreaSpotDetailModel) GetData() ([]interface{}, error) {
	sData := make([]ErmcpAreaSpotDetailModel, 0)
	err := db.GetEngine().SQL(r.buildSql()).Find(&sData)
	iDatas := make([]interface{}, 0)
	for i := range sData {
		// 注意要传指针类型(&sData[i])，因为要接口转换
		iDatas = append(iDatas, &sData[i])
	}
	return iDatas, err
}

///////////////////////////

/*历史/历史敞口*/
type ErmcpHisExposure struct {
	RECKONDATE         string  `json:"reckondate"  xorm:"'RECKONDATE'"`                 // 日照时期(yyyyMMdd)
	MIDDLEGOODSID      int32   `json:"middlegoodsid"  xorm:"'MIDDLEGOODSID'"`           // 套保品种ID
	AREAUSERID         int32   `json:"areauserid"  xorm:"'AREAUSERID'"`                 // 所属机构
	TOTALSPOTQTY       float64 `json:"totalspotqty"  xorm:"'TOTALSPOTQTY'"`             // 现货头寸总量
	TOTALFUTUREQTY     float64 `json:"totalfutureqty"  xorm:"'TOTALFUTUREQTY'"`         // 期货头寸总量
	TOTALEXPOSURE      float64 `json:"totalexposure"  xorm:"'TOTALEXPOSURE'"`           // 总敞口
	TOTALHEDGERATIO    float64 `json:"totalhedgeratio"  xorm:"'TOTALHEDGERATIO'"`       // 敞口比例
	TOTALNEEDHEDGEQTY  float64 `json:"totalneedhedgeqty"  xorm:"'TOTALNEEDHEDGEQTY'"`   // 应套保总量
	NEEDHEDGEEXPOSOURE float64 `json:"needhedgeexposourE"  xorm:"'NEEDHEDGEEXPOSOURE'"` // 应套保敞口
	NEEDHEDGERATIO     float64 `json:"needhedgeratio"  xorm:"'NEEDHEDGERATIO'"`         // 应套保敞口比例
	MIDDLEGOODSNAME    string  `json:"middlegoodsname"  xorm:"'MIDDLEGOODSNAME'"`       // 套保品种名称
	MIDDLEGOODSCODE    string  `json:"middlegoodscode"  xorm:"'MIDDLEGOODSCODE'"`       // 套保品种代码
	LastNum            int32   `json:"-"`                                               // 查询条数
}

// 历史分品种嵌套结构
type ErmcpHisExposureS struct {
	MIDDLEGOODSID   int32              `json:"middlegoodsid"`   // 套保品种ID
	MIDDLEGOODSNAME string             `json:"middlegoodsname"` // 套保品种名称
	Data            []ErmcpHisExposure `json:"data"`            // 历史敞口
}

func (r *ErmcpHisExposure) buildSql() string {
	str := "select *" +
		"  from (select t.RECKONDATE," +
		"               t.MIDDLEGOODSID," +
		"               t.AREAUSERID," +
		"               t.TOTALSPOTQTY," +
		"               t.TOTALFUTUREQTY," +
		"               t.TOTALEXPOSURE," +
		"               t.TOTALHEDGERATIO," +
		"               t.TOTALNEEDHEDGEQTY," +
		"               t.NEEDHEDGEEXPOSOURE," +
		"               t.NEEDHEDGERATIO," +
		"               g.MIDDLEGOODSNAME," +
		"               g.MIDDLEGOODSCODE" +
		"          from Reckon_ERMCP_AreaExposure t" +
		"          left join erms_middlegoods g" +
		"            on t.middlegoodsid = g.middlegoodsid" +
		"         where t.areauserid = %v" +
		"         order by t.middlegoodsid, t.reckondate desc) a" +
		" where rownum <= %v"
	if r.LastNum <= 0 {
		r.LastNum = 100000 //限制最多查10W条
	}
	return fmt.Sprintf(str, r.AREAUSERID, r.LastNum)
}

// 获取历史敞口
func (r *ErmcpHisExposure) GetData() ([]ErmcpHisExposure, error) {
	e := db.GetEngine()
	sData := make([]ErmcpHisExposure, 0)
	if err := e.SQL(r.buildSql()).Find(&sData); err != nil {
		return nil, err
	}
	return sData, nil
}
