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.Diagnostics;
using System.Linq;
using System.Text;

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

    public static BindingList<SegmentInStore> GetInStoreByStateList(int state)
    {
      var query = new DQueryDom(new JoinAlias("_main", typeof(SegmentInStore)));
      query.Columns.Add(DQSelectColumn.Field("ID"));
      query.Columns.Add(DQSelectColumn.Field("RowIndex"));
      query.Columns.Add(DQSelectColumn.Field("BarCode"));
      query.Columns.Add(DQSelectColumn.Field("Goods_Name"));
      query.Columns.Add(DQSelectColumn.Field("Goods_Spec"));
      query.Columns.Add(DQSelectColumn.Field("Weight"));
      if (state == 0)
      {
        query.Columns.Add(DQSelectColumn.Field("InStoreTime"));
        query.Range = SelectRange.Top(30);
      }
      else
        query.Columns.Add(DQSelectColumn.Field("ProductTime"));
      query.Where.Conditions.Add(DQCondition.EQ("State", state));
      if (state == 0)
        query.Where.Conditions.Add(DQCondition.Between("InStoreTime", DateTime.Today, DateTime.Today + new TimeSpan(23, 59, 59)));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("RowIndex", true));
      var list = new List<SegmentInStore>();
      using (var session = DmoSession.New())
      {
        using (var reader = session.ExecuteReader(query))
        {
          while (reader.Read())
          {
            var entity = new SegmentInStore();
            entity.ID = (long)reader[0];
            entity.RowIndex = (int?)reader[1];
            entity.BarCode = (string)reader[2];
            entity.Goods_Name = (string)reader[3];
            entity.Goods_Spec = (string)reader[4];
            entity.Weight = (decimal?)reader[5];
            if (state == 0)
              entity.InStoreTime = (DateTime)reader[6];
            else
              entity.ProductTime = (DateTime?)reader[6];
            list.Add(entity);
          }
        }
      }
      return new BindingList<SegmentInStore>(list);
    }

    public static BindingList<SegmentCodeError> GetExceptionList()
    {
      var query = new DmoQuery(typeof(SegmentCodeError));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID", true));
      query.Range = SelectRange.Top(10);
      using (var session = DmoSession.New())
      {
        return new BindingList<SegmentCodeError>(session.ExecuteList(query).Cast<SegmentCodeError>().ToList());
      }
    }

    public static void InsertSegmentCodeError(SegmentCodeError entity)
    {
      using (var session = DmoSession.New())
      {
        session.Insert(entity);
        session.Commit();
      }
    }

    public static List<SegmentInStore> GetUnInStoreList(long? maxID = null)
    {
      try
      {
        var json = RpcFacade.Call<string>(RpcPath + "GetUnInStoreList", 50, maxID, "[_goods].[Name] not like '%冻%'");
        var list = JsonConvert.DeserializeObject<List<SegmentInStore>>(json);
        var idx = 1;
        foreach (var item in list)
        {
          item.RowIndex = idx;
          idx++;
        }
        return list;
      }
      catch
      {
#if DEBUG
        throw;
#endif
        return new List<SegmentInStore>();
      }
    }

    public static string InsertInStore(SegmentInStore entity)
    {
      using (var session = DmoSession.New())
      {
        var localData = GetLocalData(session, entity.BarCode);
        entity.InStoreTime = DateTime.Now;
        if (localData == null)
        {
          session.Insert(entity);
        }
        else
        {
          if (localData.Item2 == -1)  //if delete LoadFromLocal
          {
            entity.ID = localData.Item1;
            entity.State = 0;
            Update(session, entity.ID,
              new Tuple<string, object>("Store_ID", entity.Store_ID),
              new Tuple<string, object>("InStoreTime", entity.InStoreTime),
              new Tuple<string, object>("RowIndex", entity.RowIndex),
              new Tuple<string, object>("State", 0)
              );
            FillFromLocal(session, entity);
          }
          else
          {
            //if inStored Error  已入库
            //if isBacking Error 正在退库
            //if back Error 已退库
            var msg = "重复入库";
            switch (localData.Item2)
            {
              case 1:
                msg = "正在退库";
                break;
              case 2:
                msg = "已退库";
                break;
            }
            return msg;
          }
        }
        session.Commit();
        return null;
      }
    }

    static void FillFromLocal(IDmoSession session, SegmentInStore obj)
    {
      var query = new DQueryDom(new JoinAlias(typeof(SegmentInStore)));
      query.Columns.Add(DQSelectColumn.Field("Goods_Name"));
      query.Columns.Add(DQSelectColumn.Field("Goods_Spec"));
      query.Columns.Add(DQSelectColumn.Field("Weight"));
      query.Columns.Add(DQSelectColumn.Field("ProductTime"));
      query.Range = SelectRange.Top(1);
      query.Where.Conditions.Add(DQCondition.EQ("ID", obj.ID));
      using (var reader = session.ExecuteReader(query))
      {
        if (reader.Read())
        {
          obj.Goods_Name = (string)reader[0];
          obj.Goods_Spec = (string)reader[1];
          obj.Weight = (decimal?)reader[2];
          obj.ProductTime = (DateTime?)reader[3];
        }
      }
    }

    static Tuple<long, int> GetLocalData(IDmoSession session, string barCode)
    {
      var query = new DQueryDom(new JoinAlias(typeof(SegmentInStore)));
      query.Columns.Add(DQSelectColumn.Field("ID"));
      query.Columns.Add(DQSelectColumn.Field("State"));
      query.Where.Conditions.Add(DQCondition.EQ("BarCode", barCode));
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID", true));
      query.Range = SelectRange.Top(1);
      return query.EExecuteScalar<long, int>(session);
    }

    public static string SetAsBacking(string barCode, int rowIndex)
    {
      using (var session = DmoSession.New())
      {
        var localData = GetLocalData(session, barCode);
        if (localData == null)
          return "未入库，无法退库";
        switch (localData.Item2)
        {
          case -1:
            return "已删除，无法退库";
          case 1:
            return "已在退库中，等待提交";
          case 2:
            return "已退库，无法重复退库";
          default:
            Update(session, localData.Item1,
              new Tuple<string, object>("State", 1),
               new Tuple<string, object>("RowIndex", rowIndex));
            session.Commit();
            return string.Empty;
        }
      }
    }

    public static void Delete(long id)
    {
      using (var session = DmoSession.New())
      {
        Update(session, id, new Tuple<string, object>("State", -1),
          new Tuple<string, object>("InStoreTime", null),
          new Tuple<string, object>("Store_ID", null));
        session.Commit();
      }
    }

    public static void SubmitBackStore(IEnumerable<long> ids)
    {
      using (var session = DmoSession.New())
      {
        BatchUpdate(session, ids, new Tuple<string, object>("State", 2), new Tuple<string, object>("BackStoreTime", DateTime.Now));
        session.Commit();
      }
    }

    static void Update(IDmoSession session, long id, params Tuple<string, object>[] tuple)
    {
      var update = new DQUpdateDom(typeof(SegmentInStore));
      update.Where.Conditions.Add(DQCondition.EQ("ID", id));

      foreach (var item in tuple)
        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);
    }

    static void BatchUpdate(IDmoSession session, IEnumerable<long> ids, params Tuple<string, object>[] tuple)
    {
      var update = new DQUpdateDom(typeof(SegmentInStore));
      update.Where.Conditions.Add(DQCondition.InList(DQExpression.Field("ID"), ids.Select(x => DQExpression.Value(x)).ToArray()));

      foreach (var item in tuple)
        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 BindingList<SegmentInStore> GetInStoreSummary()
    {
      var query = new DQueryDom(new JoinAlias("_main", typeof(SegmentInStore)));
      query.Columns.Add(DQSelectColumn.Field("Goods_Code"));
      query.Columns.Add(DQSelectColumn.Field("Goods_Name"));
      query.Columns.Add(DQSelectColumn.Field("Goods_Spec"));
      query.Columns.Add(DQSelectColumn.Count());
      query.Columns.Add(DQSelectColumn.Sum("Weight"));
      query.GroupBy.Expressions.Add(DQExpression.Field("Goods_Code"));
      query.GroupBy.Expressions.Add(DQExpression.Field("Goods_Name"));
      query.GroupBy.Expressions.Add(DQExpression.Field("Goods_Spec"));

      query.Where.Conditions.Add(DQCondition.Between("InStoreTime", DateTime.Today, DateTime.Today + new TimeSpan(23, 59, 59)));
      query.Where.Conditions.Add(DQCondition.Or(DQCondition.EQ("State", 0), DQCondition.EQ("State", 1)));

      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("Goods_Code"));
      var list = new BindingList<SegmentInStore>();
      using (var session = DmoSession.New())
      {
        using (var reader = session.ExecuteReader(query))
        {
          var idx = 1;
          while (reader.Read())
          {
            var entity = new SegmentInStore();
            entity.RowIndex = idx;
            entity.Goods_Code = (string)reader[0];
            entity.Goods_Name = (string)reader[1];
            entity.Goods_Spec = (string)reader[2];
            entity.Number = Convert.ToInt32(reader[3]);
            entity.Weight = (decimal?)reader[4];
            list.Add(entity);
            idx++;
          }
        }
      }
      return list;
    }

    public static void UploadSegmentInstoreInfo()
    {
      using (var session = DmoSession.New())
      {
        var needUpload = GetUnSyncData(session);
        if (needUpload.Count == 0)
          return;

        var json = JsonConvert.SerializeObject(needUpload);
        RpcFacade.Call<int>(RpcPath + "UploadSegmentInStoreInfo", json);
        foreach (var item in needUpload)
          SetLocalAsSyncd(item, session);
        session.Commit();
      }
    }

    static List<SegmentInStoreObj> GetUnSyncData(IDmoSession session)
    {
      var query = new DQueryDom(new JoinAlias(typeof(SegmentInStore)));
      query.Columns.Add(DQSelectColumn.Field("ID"));
      query.Columns.Add(DQSelectColumn.Field("RowVersion"));
      query.Columns.Add(DQSelectColumn.Field("BarCode"));
      query.Columns.Add(DQSelectColumn.Field("InStoreTime"));
      query.Columns.Add(DQSelectColumn.Field("Store_ID"));
      query.Columns.Add(DQSelectColumn.Field("BackStoreTime"));
      query.Where.Conditions.Add(DQCondition.And(DQCondition.InEQ("State", 1), DQCondition.EQ("Sync", false), DQCondition.IsNotNull(DQExpression.Field("Weight"))));
      query.Range = SelectRange.Top(10);
      query.OrderBy.Expressions.Add(DQOrderByExpression.Create("ID"));

      var upload = new List<SegmentInStoreObj>();
      using (var reader = session.ExecuteReader(query))
      {
        while (reader.Read())
        {
          var obj = new SegmentInStoreObj();
          obj.ID = (long)reader[0];
          obj.RowVersion = (int)reader[1];
          obj.BarCode = (string)reader[2];
          obj.InStoreTime = (DateTime?)reader[3];
          obj.Store_ID = (long?)reader[4];
          obj.BackStoreTime = (DateTime?)reader[5];
          upload.Add(obj);
        }
      }
      return upload;
    }

    static void SetLocalAsSyncd(SegmentInStoreObj obj, IDmoSession session)
    {
      var update = new DQUpdateDom(typeof(SegmentInStore));
      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);
    }

    public static void ClearErrorMsg()
    {
      var sql = string.Format(@"truncate table [{0}]", DmoInfo.Get(typeof(SegmentCodeError)).MappedDBObject);
      using (var session = DmoSession.New())
      {
        session.ExecuteSqlNonQuery(sql);
        session.Commit();
      }
    }

    public static void RefreshFromServer(SegmentInStore entity)
    {
      try
      {
        var json = RpcFacade.Call<string>(RpcPath + "GetBarInfoByCode", entity.BarCode);
        if (string.IsNullOrEmpty(json))
          return;
        var obj = JsonConvert.DeserializeObject<SegmentInStore>(json);
        entity.Goods_Name = obj.Goods_Name;
        entity.Goods_Spec = obj.Goods_Spec;
        entity.ProductTime = obj.ProductTime;
        entity.Weight = obj.Weight;
        SimpleUpdate(entity);
      }
      catch
      {
#if DEBUG
        throw;
#endif
      }
    }

    public static void SimpleUpdate(SegmentInStore entity)
    {
      var update = new DQUpdateDom(typeof(SegmentInStore));
      update.Where.Conditions.Add(DQCondition.EQ("ID", entity.ID));
      update.Columns.Add(new DQUpdateColumn("Goods_Name", entity.Goods_Name));
      update.Columns.Add(new DQUpdateColumn("Goods_Spec", entity.Goods_Spec));
      update.Columns.Add(new DQUpdateColumn("ProductTime", entity.ProductTime));
      update.Columns.Add(new DQUpdateColumn("Weight", entity.Weight));
      update.EExecute();
    }

    public static void DeleteEmpty(long id)
    {
      var delete = new DQDeleteDom(typeof(SegmentInStore));
      delete.Where.Conditions.Add(DQCondition.EQ("ID", id));
      delete.EExecute();
    }
  }

  class SegmentInStoreObj
  {
    [JsonIgnore]
    public long ID { get; set; }

    [JsonIgnore]
    public long RowVersion { get; set; }

    public string BarCode { get; set; }

    public DateTime? InStoreTime { get; set; }

    public long? Store_ID { get; set; }

    public DateTime? BackStoreTime { get; set; }
  }
}
