using ButcherFactory.BO.Utils;
using Forks.EnterpriseServices.DomainObjects2;
using Forks.EnterpriseServices.DomainObjects2.DQuery;
using Forks.JsonRpc.Client;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ButcherFactory.BO.LocalBL
{
  public static class CarcassSaleOutBL
  {
    const string RpcPath = @"/MainSystem/B3Sale/Rpcs/";
    const string MESPath = @"/MainSystem/B3ClientService/Rpcs/";

    public static BindingList<SaleOutStore> GetSaleOutStoreList(DateTime sendDate, long? deliverGoodsLineID, long? customerID, int billState, long? storeID, bool already = false)
    {
      var json = RpcFacade.Call<string>(RpcPath + "SaleOutStoreRpc/GetSaleOutStoreList", sendDate, billState, deliverGoodsLineID, customerID, storeID, already);
      var list = JsonConvert.DeserializeObject<List<SaleOutStore>>(json);
      return new BindingList<SaleOutStore>(list);
    }

    public static BindingList<SaleOutStore_Detail> GetSaleOutStoreDetailList(long id)
    {
      var json = RpcFacade.Call<string>(RpcPath + "SaleOutStoreRpc/GetSaleOutStoreDetailList", id);
      var list = JsonConvert.DeserializeObject<List<SaleOutStore_Detail>>(json);
      return new BindingList<SaleOutStore_Detail>(list);
    }

    public static BindingList<CarcassSaleOut_Detail> GetWeightRecord(long detailID)
    {
      var query = new DmoQuery(typeof(CarcassSaleOut_Detail));
      query.Where.Conditions.Add(DQCondition.EQ("DetailID", detailID));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID", true));
      var list = query.EExecuteList().Cast<CarcassSaleOut_Detail>().ToList();
      var idx = list.Count;
      foreach (var item in list)
      {
        item.Idx = idx;
        idx--;
      }
      return new BindingList<CarcassSaleOut_Detail>(list);
    }

    public static CarcassSaleOut_Detail Insert(decimal weight)
    {
      using (var session = DmoSession.New())
      {
        var detail = new CarcassSaleOut_Detail() { Weight = weight, Number = 1 };
        session.Insert(detail);
        session.Commit();
        return detail;
      }
    }

    public static void InsertPackage(CarcassSaleOut_Detail detail)
    {
      var gInfo = GetGoodsInfo(detail.Goods_ID.Value);
      detail.Goods_Name = gInfo.Item1;
      detail.Goods_Code = gInfo.Item2;
      using (var session = DmoSession.New())
      {
        session.Insert(detail);
        session.Commit();
      }
    }

    public static bool BarCodeUsed(string barCode)
    {
      var query = new DQueryDom(new JoinAlias(typeof(CarcassSaleOut_Detail)));
      query.Where.Conditions.Add(DQCondition.EQ("BarCode", barCode));
      query.Columns.Add(DQSelectColumn.Create(DQExpression.Value(1), "c"));
      //query.Having.Conditions.Add(DQCondition.EQ(DQExpression.Sum(DQExpression.LogicCase(DQCondition.EQ("IsBack", true), DQExpression.Value(-1), DQExpression.Value(1))), DQExpression.Value(0)));
      return query.EExecuteScalar() != null;
    }

    public static BindingList<CarcassSaleOut_Detail> GetUnSubmitWeightRecord()
    {
      var query = new DmoQuery(typeof(CarcassSaleOut_Detail));
      query.Where.Conditions.Add(DQCondition.IsNull(DQExpression.Field("DetailID")));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID", true));
      var list = query.EExecuteList().Cast<CarcassSaleOut_Detail>().ToList();
      var idx = list.Count;
      foreach (var item in list)
      {
        item.Idx = idx;
        idx--;
      }
      return new BindingList<CarcassSaleOut_Detail>(list);
    }

    public static void FillDetail(CarcassSaleOut_Detail first, string barCode, long? batchID)
    {
      var list = new List<Tuple<string, object>>();
      if (barCode.StartsWith("G"))
      {
        var arr = barCode.TrimStart('G').Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);

        var gid = long.Parse(arr[0]);
        var gInfo = GetGoodsInfo(gid);
        first.Goods_ID = gid;
        first.Goods_Name = gInfo.Item1;
        first.Goods_Code = gInfo.Item2;
        first.ProductBatch_ID = batchID;
        if (arr.Length == 2)
        {
          first.Number = 0.5m;
          list.Add(new Tuple<string, object>("Number", first.Number));
        }
        list.Add(new Tuple<string, object>("ProductBatch_ID", first.ProductBatch_ID));
      }
      else
      {
        string json;
        try
        {
          json = RpcFacade.Call<string>("/MainSystem/B3ButcherManage/Rpcs/CarcassStoreDetailRpc/GetCarcassInstoreInfo", barCode);
        }
        catch
        {
          throw;
        }
        var mesInfo = JsonConvert.DeserializeObject<SaleOutCarcassObj>(json);
        if (!string.IsNullOrEmpty(mesInfo.Goods_Code))
        {
          if (mesInfo.Goods_Code == "X002")//特殊处理，有个存货编码不存在。
            mesInfo.Goods_Code = "0001";
          var gInfo = GetGoodsInfo(mesInfo.Goods_Code);
          first.Goods_Code = mesInfo.Goods_Code;
          first.InStoreWeight = mesInfo.InStoreWeight;
          first.Number = mesInfo.Number;
          first.Goods_ID = gInfo.Item1;
          first.Goods_Name = gInfo.Item2;
        }
        first.BarCode = barCode;
        list.Add(new Tuple<string, object>("BarCode", first.BarCode));
        list.Add(new Tuple<string, object>("InStoreWeight", first.InStoreWeight));
        list.Add(new Tuple<string, object>("Number", first.Number));
      }
      first.Filled = true;
      list.Add(new Tuple<string, object>("Goods_ID", first.Goods_ID));
      list.Add(new Tuple<string, object>("Goods_Name", first.Goods_Name));
      list.Add(new Tuple<string, object>("Goods_Code", first.Goods_Code));
      list.Add(new Tuple<string, object>("Filled", first.Filled));
      using (var session = DmoSession.New())
      {
        Update(session, first.ID, list.ToArray());
        session.Commit();
      }
    }

    public static void UpdateWeightNumber(long id, decimal number)
    {
      using (var session = DmoSession.New())
      {
        Update(session, id, new Tuple<string, object>("Number", number));
        session.Commit();
      }
    }

    static void Update(IDmoSession session, long id, params Tuple<string, object>[] pops)
    {
      var update = new DQUpdateDom(typeof(CarcassSaleOut_Detail));
      foreach (var item in pops)
        update.Columns.Add(new DQUpdateColumn(item.Item1, item.Item2));
      update.Where.Conditions.Add(DQCondition.EQ("ID", id));
      session.ExecuteNonQuery(update);
    }

    static Dictionary<long, Tuple<string, string>> goodsIdInfo = new Dictionary<long, Tuple<string, string>>();
    static Tuple<string, string> GetGoodsInfo(long id)
    {
      if (!goodsIdInfo.ContainsKey(id))
      {
        var json = RpcFacade.Call<string>(RpcPath + "BaseInfoSelectRpc/GetGoodsInfo", "ID", id);
        var g = JsonConvert.DeserializeObject<ExtensionObj>(json);
        if (g.LongExt1 == null)
          throw new Exception("没有找到存货No." + id);
        goodsIdInfo.Add(id, new Tuple<string, string>(g.StringExt1, g.StringExt2));
      }
      return goodsIdInfo[id];
    }

    static Dictionary<string, Tuple<long, string>> goodsCodeInfo = new Dictionary<string, Tuple<long, string>>();
    public static Tuple<long, string> GetGoodsInfo(string code)
    {
      if (!goodsCodeInfo.ContainsKey(code))
      {
        var json = RpcFacade.Call<string>(RpcPath + "BaseInfoSelectRpc/GetGoodsInfo", "Code", code);
        var g = JsonConvert.DeserializeObject<ExtensionObj>(json);
        if (g.LongExt1 == null)
          throw new Exception("没有找到存货编码 " + code);
        goodsCodeInfo.Add(code, new Tuple<long, string>(g.LongExt1.Value, g.StringExt1));
      }
      return goodsCodeInfo[code];
    }

    public static List<ProductBatch> GetBatchFromEMS(int type)
    {
      var json = ButcherFactoryUtil.SecondUrlCall<string>(MESPath + "SyncBaseInfoRpc/GetProductBatchByType", 9, type);
      return JsonConvert.DeserializeObject<List<ProductBatch>>(json);
    }

    public static void SubmitDetails(IEnumerable<CarcassSaleOut_Detail> details, SaleOutStore_Detail detail,DateTime? mProductTime)
    {
      var arr = details.Select(x => new WeightRecord { Flag = (!string.IsNullOrEmpty(x.BarCode) && x.BarCode.StartsWith("P")) ? -1 : 0, ID = x.ID, WeightTime = x.Time, MainUnitNum = x.Weight, SecondNumber = x.Number, ProductBatch_ID = x.ProductBatch_ID, BarCode = x.BarCode, ProductTime = mProductTime });
      var json = RpcFacade.Call<string>(RpcPath + "SaleOutStoreRpc/SaveWeightRecord", JsonConvert.SerializeObject(arr), detail.ID);
      var back = JsonConvert.DeserializeObject<Tuple<decimal?, decimal?, List<ExtensionObj>>>(json);
      using (var session = DmoSession.New())
      {
        foreach (var item in details)
        {
          var first = back.Item3.First(x => x.LongExt1 == item.ID);

          Update(session, item.ID, new Tuple<string, object>("BillID", detail.SaleOutStore_ID),
            new Tuple<string, object>("DetailID", detail.ID),
            new Tuple<string, object>("WeightRecord_ID", first.LongExt2),
            new Tuple<string, object>("ScanRecord_ID", first.LongExt3)
            );
        }
        session.Commit();
      }
      detail.SNumber = back.Item1;
      detail.SSecondNumber = back.Item2;
    }

    public static void SetGoodsFinish(long id)
    {
      RpcFacade.Call<int>(RpcPath + "SaleOutStoreRpc/SetFinishAssignState", id, true);
    }

    public static void SetGoodsUnFinish(long id)
    {
      RpcFacade.Call<int>(RpcPath + "SaleOutStoreRpc/SetFinishAssignState", id, false);
    }

    public static void Delete(long id)
    {
      using (var session = DmoSession.New())
      {
        var item = session.Load(new DmoIdentity(typeof(CarcassSaleOut_Detail),id)) as CarcassSaleOut_Detail;
        SaveDeleteInfo(session, item);
        Delete(session, id);
        session.Commit();
      }
    }

    static void Delete(IDmoSession session, long id)
    {
      var delete = new DQDeleteDom(typeof(CarcassSaleOut_Detail));
      delete.Where.Conditions.Add(DQCondition.EQ("ID", id));
      session.ExecuteNonQuery(delete);
    }

    public static void AddAndUpdate(CarcassSaleOut_Detail detail, int flag,DateTime? productTime)
    {
      using (var session = DmoSession.New())
      {
        session.Insert(detail);
        var arr = new List<WeightRecord> { new WeightRecord { Flag = flag, ID = detail.ID, WeightTime = detail.Time, MainUnitNum = detail.Weight, SecondNumber = detail.Number, ProductTime = productTime } };
        var json = RpcFacade.Call<string>(RpcPath + "SaleOutStoreRpc/SaveWeightRecord", JsonConvert.SerializeObject(arr), detail.DetailID);
        var back = JsonConvert.DeserializeObject<Tuple<decimal?, decimal?, List<ExtensionObj>>>(json);

        var first = back.Item3.First();

        Update(session, detail.ID, new Tuple<string, object>("WeightRecord_ID", first.LongExt2),
          new Tuple<string, object>("ScanRecord_ID", first.LongExt3)
          );

        session.Commit();
      }
    }

    public static void DeleteAndUpdate(CarcassSaleOut_Detail tag)
    {
      var json = JsonConvert.SerializeObject(new List<ExtensionObj>() { new ExtensionObj { LongExt1 = tag.DetailID, LongExt2 = tag.WeightRecord_ID, LongExt3 = tag.ScanRecord_ID } });
      RpcFacade.Call<int>(RpcPath + "SaleOutStoreRpc/DeleteAndUpdate", json);
      using (var session = DmoSession.New())
      {
        SaveDeleteInfo(session, tag);
        Delete(session, tag.ID);
        session.Commit();
      }
    }

    static void SaveDeleteInfo(IDmoSession session, CarcassSaleOut_Detail tag)
    {
      var delete = new CarcassSaleOut_Delete();
      var type = typeof(CarcassSaleOut_Delete);
      foreach (var p in tag.GetType().GetProperties())
      {
        if (p.CanWrite)
          type.GetProperty(p.Name).SetValue(delete, p.GetValue(tag));
      }
      delete.DeleteTime = DateTime.Now;
      delete.Deleter = BO.Utils.AppContext.Worker.Name;
      session.Insert(delete);
    }

    public static void RollBackDetails(List<CarcassSaleOut_Detail> backList)
    {
      var list = backList.Select(x => new ExtensionObj { LongExt1 = x.DetailID, LongExt2 = x.WeightRecord_ID, LongExt3 = x.ScanRecord_ID });
      RpcFacade.Call<int>(RpcPath + "SaleOutStoreRpc/DeleteAndUpdate", JsonConvert.SerializeObject(list));

      var update = new DQUpdateDom(typeof(CarcassSaleOut_Detail));
      update.Where.Conditions.Add(DQCondition.InList(DQExpression.Field("ID"), backList.Select(x => DQExpression.Value(x.ID)).ToArray()));
      update.Columns.Add(new DQUpdateColumn("BillID", DQExpression.NULL));
      update.Columns.Add(new DQUpdateColumn("DetailID", DQExpression.NULL));
      update.Columns.Add(new DQUpdateColumn("WeightRecord_ID", DQExpression.NULL));
      update.Columns.Add(new DQUpdateColumn("ScanRecord_ID", DQExpression.NULL));
      update.EExecute();
    }

    public static bool HasNoAssignDetail(long billid)
    {
      return RpcFacade.Call<bool>(RpcPath + "SaleOutStoreRpc/HasNoAssignDetail", billid);
    }

    public static BindingList<CarcassSaleOut_Delete> GetDeletedWeightRecord(long detailID)
    {
      var query = new DmoQuery(typeof(CarcassSaleOut_Delete));
      query.Where.Conditions.Add(DQCondition.EQ("DetailID", detailID));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID", true));
      var list = query.EExecuteList().Cast<CarcassSaleOut_Delete>().ToList();
      var idx = list.Count;
      foreach (var item in list)
      {
        item.Idx = idx;
        idx--;
      }
      return new BindingList<CarcassSaleOut_Delete>(list);
    }

    #region 静态称发货

    //插入包括条码的记录 条码、批次、数量为1 、重量为0 Fill为否
    public static CarcassSaleOut_Detail ReadBarCode(string barCode, long? batchID, bool isBack)
    {
      using (var session = DmoSession.New())
      {
        var detail = new CarcassSaleOut_Detail();
        if (barCode.StartsWith("G"))
        {
          var arr = barCode.TrimStart('G').Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);

          var gid = long.Parse(arr[0]);
          var gInfo = GetGoodsInfo(gid);
          detail.Goods_ID = gid;
          detail.Goods_Name = gInfo.Item1;
          detail.Goods_Code = gInfo.Item2;
          detail.ProductBatch_ID = batchID;
          if (arr.Length == 2)
            detail.Number = 0.5m;
          else
            detail.Number = 1;
        }
        else
        {
          var json = ButcherFactoryUtil.SecondUrlCall<string>(MESPath + "CarcassSaleOutStoreRpc/GetCarcassInstoreInfo", barCode);
          var mesInfo = JsonConvert.DeserializeObject<SaleOutCarcassObj>(json);
          if (!string.IsNullOrEmpty(mesInfo.Goods_Code))
          {
            if (mesInfo.Goods_Code == "X002")//特殊处理，有个存货编码不存在。
              mesInfo.Goods_Code = "0001";
            var gInfo = GetGoodsInfo(mesInfo.Goods_Code);
            detail.Goods_Code = mesInfo.Goods_Code;
            detail.InStoreWeight = mesInfo.InStoreWeight;
            detail.Goods_ID = gInfo.Item1;
            detail.Goods_Name = gInfo.Item2;
            detail.Number = 1;
          }
          detail.BarCode = barCode;
        }
        session.Insert(detail);
        session.Commit();
        return detail;
      }
    }

    //为一组重量为空（或加个标识）的数据赋值重量
    public static void InsertWeight(List<long> ids, decimal weight)
    {
      var last = ids.Last();
      ids.Remove(last);
      using (var session = DmoSession.New())
      {
        Update(session, last, new Tuple<string, object>("Filled", true), new Tuple<string, object>("Weight", weight));
        if (ids.Any())
          BatchUpdate(session, ids, new Tuple<string, object>("Filled", true), new Tuple<string, object>("Weight", 0));
        session.Commit();
      }
    }

    static void BatchUpdate(IDmoSession session, IEnumerable<long> ids, params Tuple<string, object>[] pops)
    {
      var update = new DQUpdateDom(typeof(CarcassSaleOut_Detail));
      foreach (var item in pops)
        update.Columns.Add(new DQUpdateColumn(item.Item1, item.Item2));
      update.Where.Conditions.Add(DQCondition.InList(DQExpression.Field("ID"), ids.Select(x => DQExpression.Value(x)).ToArray()));
      session.ExecuteNonQuery(update);
    }

    //自动发货，包括重量，默认码。只有一条数据
    public static CarcassSaleOut_Detail AutoFill(string barCode, long? batchID, decimal weight)
    {
      using (var session = DmoSession.New())
      {
        var detail = new CarcassSaleOut_Detail();

        var gid = long.Parse(barCode.TrimStart('G'));
        var gInfo = GetGoodsInfo(gid);
        detail.Goods_ID = gid;
        detail.Goods_Name = gInfo.Item1;
        detail.Goods_Code = gInfo.Item2;
        detail.ProductBatch_ID = batchID;
        detail.Number = 1;
        detail.Filled = true;
        detail.Weight = weight;
        session.Insert(detail);
        session.Commit();
        return detail;
      }
    }

    #endregion
  }

  class SaleOutCarcassObj
  {
    public string Goods_Code { get; set; }

    public decimal? InStoreWeight { get; set; }
    public decimal Number { get; set; }
  }

  class WeightRecord
  {
    //0、白条；1、分割品；2、副产品；
    public int Flag { get; set; }
    public long ID { get; set; }
    public string BarCode { get; set; }
    public long? ProductBatch_ID { get; set; }
    public DateTime WeightTime { get; set; }
    public decimal? MainUnitNum { get; set; }
    public decimal? SecondNumber { get; set; }
    public DateTime? ProductTime { get; set; }
  }
}
