using ButcherFactory.BO.Utils;
using Forks.EnterpriseServices.DomainObjects2;
using Forks.EnterpriseServices.DomainObjects2.DQuery;
using Forks.EnterpriseServices.SqlDoms;
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 CarcassTakeOutBL
  {
    const string RpcPath = @"/MainSystem/B3ClientService/Rpcs/CarcassTakeOutRpc/";

    public static CarcassTakeOut Insert(long? workUnitID, long? batchID, long? goodsID, string barCode,long storeID)
    {
      using (var session = DmoSession.New())
      {
        var exist = CheckExist(barCode, session);
        if (exist)
          return null;
        var entity = new CarcassTakeOut();
        entity.WorkUnit_ID = workUnitID;
        if (string.IsNullOrEmpty(barCode))
        {
          entity.ProductBatch_ID = batchID;
          entity.Goods_ID = goodsID;
        }
        entity.Number = 1;
        entity.Store_ID = storeID;
        entity.UserID = BO.Utils.AppContext.Worker.ID;
        entity.BarCode = barCode;
        entity.RowIndex = GenerateRowIndex(session);
        session.Insert(entity);
        session.Commit();
        return entity;
      }
    }

    static int GenerateRowIndex(IDmoSession session)
    {
      var query = new DQueryDom(new JoinAlias("_main", typeof(CarcassTakeOut)));
      query.Columns.Add(DQSelectColumn.Max("RowIndex"));
      query.Where.Conditions.Add(DQCondition.EQ(DQExpression.Snippet("CAST([_main].[CreateTime] AS DATE)"), DQExpression.Value(DateTime.Today)));
      return (query.EExecuteScalar<int?>(session) ?? 0) + 1;
    }

    static bool CheckExist(string barCode, IDmoSession session)
    {
      if (string.IsNullOrEmpty(barCode))
        return false;
      var query = new DQueryDom(new JoinAlias(typeof(CarcassTakeOut)));
      query.Columns.Add(DQSelectColumn.Create(DQExpression.Value(1), "c"));
      query.Where.Conditions.Add(DQCondition.EQ("BarCode", barCode));
      return query.EExecuteScalar(session) != null;
    }

    static void Update(long id, IDmoSession session, params Tuple<string, object>[] updates)
    {
      var update = new DQUpdateDom(typeof(CarcassTakeOut));
      update.Where.Conditions.Add(DQCondition.EQ("ID", id));
      foreach (var item in updates)
        update.Columns.Add(new DQUpdateColumn(item.Item1, item.Item2));
      update.Columns.Add(new DQUpdateColumn("Sync", false));
      update.Columns.Add(new DQUpdateColumn("RowVersion", DQExpression.Add(DQExpression.Field("RowVersion"), DQExpression.Value(1))));
      session.ExecuteNonQuery(update);
    }

    public static void Update(long id, params Tuple<string, object>[] updates)
    {
      using (var session = DmoSession.New())
      {
        Update(id, session, updates);
        session.Commit();
      }
    }

    public static BindingList<CarcassTakeOutWeightTemp> GetWeightList()
    {
      var query = new DQueryDom(new JoinAlias(typeof(CarcassTakeOutWeightTemp)));
      query.Columns.Add(DQSelectColumn.Field("ID"));
      query.Columns.Add(DQSelectColumn.Field("Weight"));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID", true));
      var result = new BindingList<CarcassTakeOutWeightTemp>();
      query.EExecuteList<long, decimal>().ForEach(x => result.Add(new CarcassTakeOutWeightTemp { ID = x.Item1, Weight = x.Item2 }));
      return result;
    }

    public static CarcassTakeOutWeightTemp InsertWeight(decimal weight)
    {
      using (var session = DmoSession.New())
      {
        var entity = new CarcassTakeOutWeightTemp { Weight = weight };
        session.Insert(entity);
        session.Commit();
        return entity;
      }
    }

    public static BindingList<CarcassTakeOut> GetLocalDataWithState(bool history)
    {
      var query = new DQueryDom(new JoinAlias(typeof(CarcassTakeOut)));
      query.Columns.Add(DQSelectColumn.Field("RowIndex"));
      query.Columns.Add(DQSelectColumn.Field("ID"));
      query.Columns.Add(DQSelectColumn.Field("BarCode"));
      query.Columns.Add(DQSelectColumn.Field("Goods_Name"));
      query.Columns.Add(DQSelectColumn.Field("BeforeWeight"));
      query.Columns.Add(DQSelectColumn.Field("Weight"));
      query.Columns.Add(DQSelectColumn.Field("Number"));
      query.Columns.Add(DQSelectColumn.Field("IsCarcass"));
      if (history)
        query.Range = SelectRange.Top(30);
      query.Where.Conditions.Add(DQCondition.EQ("Submited", history));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID", true));
      var result = new BindingList<CarcassTakeOut>();
      using (var session = DmoSession.New())
      {
        using (var reader = session.ExecuteReader(query))
        {
          while (reader.Read())
          {
            var entity = new CarcassTakeOut();
            result.Add(entity);
            entity.RowIndex = (int?)reader[0];
            entity.ID = (long)reader[1];
            entity.BarCode = (string)reader[2];
            entity.Goods_Name = (string)reader[3];
            entity.BeforeWeight = (decimal?)reader[4];
            entity.Weight = (decimal?)reader[5];
            entity.Number = (int)reader[6];
            entity.IsCarcass = (bool)reader[7];
          }
        }
      }
      return result;
    }

    static void GroupUpdate(IEnumerable<long> ids, IDmoSession session, params Tuple<string, object>[] updates)
    {
      if (ids.Count() == 0)
        return;
      var arr = ButcherFactoryUtil.SplitList<long>(ids.ToList(), 500);
      foreach (var items in arr)
      {
        var update = new DQUpdateDom(typeof(CarcassTakeOut));
        update.Where.Conditions.Add(DQCondition.InList(DQExpression.Field("ID"), items.Select(x => DQExpression.Value(x)).ToArray()));
        foreach (var item in updates)
          update.Columns.Add(new DQUpdateColumn(item.Item1, item.Item2));
        update.Columns.Add(new DQUpdateColumn("Sync", false));
        update.Columns.Add(new DQUpdateColumn("RowVersion", DQExpression.Add(DQExpression.Field("RowVersion"), DQExpression.Value(1))));
        session.ExecuteNonQuery(update);
      }
    }

    public static void Submit(decimal weight, List<CarcassTakeOut> list)
    {
      using (var session = DmoSession.New())
      {
        var ids = list.Select(x => x.ID);
        var id = list.Max(x => x.ID);

        Update(id, session, new Tuple<string, object>("GroupID", id), new Tuple<string, object>("Weight", weight));
        GroupUpdate(ids.Where(x => x != id), session, new Tuple<string, object>("GroupID", id), new Tuple<string, object>("Weight", 0));

        foreach (var item in list)
        {
          if (item.ID == id)
            item.Weight = weight;
          else
            item.Weight = 0;
        }

        GroupUpdate(ids, session, new Tuple<string, object>("Submited", true));
        var sql = @"truncate table [Butcher_CarcassTakeOutWeightTemp];";
        session.ExecuteSqlNonQuery(sql);
        session.Commit();
      }
    }

    public static List<ExtensionObj> GetBeforeInfo(IEnumerable<string> codeList)
    {
      try
      {
        var json = RpcFacade.Call<string>(RpcPath + "GetBeforeInfo", codeList);
        var list = JsonConvert.DeserializeObject<List<ExtensionObj>>(json);
        if (list.Any())
        {
          using (var session = DmoSession.New())
          {
            foreach (var item in list)
              SaveBeforeInfoInDB(item, session);
            session.Commit();
          }
        }
        return list;
      }
      catch
      {
#if DEBUG
        throw;
#endif
        return new List<ExtensionObj>();
      }
    }

    private static void SaveBeforeInfoInDB(ExtensionObj obj, IDmoSession session)
    {
      var update = new DQUpdateDom(typeof(CarcassTakeOut));
      update.Where.Conditions.Add(DQCondition.EQ("BarCode", obj.StringExt1));
      update.Columns.Add(new DQUpdateColumn("Goods_ID", obj.LongExt1));
      update.Columns.Add(new DQUpdateColumn("BeforeWeight", obj.DecimalExt1));
      session.ExecuteNonQuery(update);
    }

    static string carcassMethod = RpcPath + "UploadCarcassInfo";
    public static void UploadCarcassInfo()
    {
      try
      {
        using (var session = DmoSession.New())
        {
          var needUpload = GetUnSyncData(session);
          if (needUpload.Count == 0)
            return;
          var json = JsonConvert.SerializeObject(needUpload);
          RpcFacade.Call<int>(carcassMethod, json);
          foreach (var item in needUpload)
            SetLocalAsSyncd(item, session);
          session.Commit();
        }
      }
      catch
      {
#if DEBUG
        throw;
#endif
      }
    }

    static List<CarcassTakeOutObj> GetUnSyncData(IDmoSession session)
    {
      var query = new DQueryDom(new JoinAlias(typeof(CarcassTakeOut)));
      query.Columns.Add(DQSelectColumn.Field("ID"));
      query.Columns.Add(DQSelectColumn.Field("RowVersion"));
      query.Columns.Add(DQSelectColumn.Field("BarCode"));
      query.Columns.Add(DQSelectColumn.Field("UserID"));
      query.Columns.Add(DQSelectColumn.Field("WorkUnit_ID"));
      query.Columns.Add(DQSelectColumn.Field("ProductBatch_ID"));
      query.Columns.Add(DQSelectColumn.Field("Goods_ID"));
      query.Columns.Add(DQSelectColumn.Field("Weight"));
      query.Columns.Add(DQSelectColumn.Field("CreateTime"));
      query.Columns.Add(DQSelectColumn.Field("GroupID"));
      query.Columns.Add(DQSelectColumn.Field("Number"));
      query.Columns.Add(DQSelectColumn.Field("IsCarcass"));
      query.Columns.Add(DQSelectColumn.Field("UserID"));
      query.Columns.Add(DQSelectColumn.Field("Store_ID"));
      query.Where.Conditions.Add(DQCondition.And(DQCondition.EQ("Submited", true), DQCondition.EQ("Sync", false)));
      query.Range = SelectRange.Top(10);
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID"));

      var upload = new List<CarcassTakeOutObj>();
      using (var reader = session.ExecuteReader(query))
      {
        while (reader.Read())
        {
          var obj = new CarcassTakeOutObj();
          obj.ID = (long)reader[0];
          obj.RowVersion = (int)reader[1];
          obj.BarCode = (string)reader[2];
          obj.TakeOutWorker_ID = (long)reader[3];
          obj.WorkUnit_ID = (long?)reader[4];
          if (string.IsNullOrEmpty(obj.BarCode))
          {
            obj.ProductBatch_ID = (long?)reader[5];
            obj.Goods_ID = (long?)reader[6];
          }
          obj.Weight = (decimal)reader[7];
          obj.Time = (DateTime)reader[8];
          obj.GroupID = (long?)reader[9];
          obj.Number = (int)reader[10];
          obj.IsCarcass = (bool)reader[11];
          obj.Worker_ID = (long)reader[12];
          obj.Store_ID = (long?)reader[13];
          upload.Add(obj);
        }
      }
      return upload;
    }

    static void SetLocalAsSyncd(CarcassTakeOutObj obj, IDmoSession session)
    {
      var update = new DQUpdateDom(typeof(CarcassTakeOut));
      update.Where.Conditions.Add(DQCondition.And(DQCondition.EQ("ID", obj.ID), DQCondition.EQ("RowVersion", obj.RowVersion)));
      update.Columns.Add(new DQUpdateColumn("Sync", true));
      session.ExecuteNonQuery(update);
    }
  }

  class CarcassTakeOutObj
  {
    [JsonIgnore]
    public long ID { get; set; }
    [JsonIgnore]
    public long RowVersion { get; set; }
    public string BarCode { get; set; }
    public long? TakeOutWorker_ID { get; set; }
    public long? WorkUnit_ID { get; set; }
    public long? ProductBatch_ID { get; set; }
    public long? Goods_ID { get; set; }
    public decimal? Weight { get; set; }
    public DateTime? Time { get; set; }
    public long? GroupID { get; set; }
    public int Number { get; set; }
    public bool IsCarcass { get; set; }
    public long Worker_ID { get; set; }
    public long? Store_ID { get; set; }
  }
}
