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

namespace ButcherFactory.BO.Rpcs
{
  public static class BaseInfoSyncRpc
  {
    const string baseInfoRpcPath = @"/MainSystem/B3ClientService/Rpcs/SyncBaseInfoRpc/";

    public static bool SyncBaseInfo<T>(string methodName = null)
      where T : BaseInfo
    {
      var type = typeof(T);
      if (string.IsNullOrEmpty(methodName))
        methodName = "Sync" + type.Name;
      using (var session = DmoSession.New())
      {
        var localVersion = GetLocalVersion<T>(session);
        var json = RpcFacade.Call<string>(baseInfoRpcPath + methodName, JsonConvert.SerializeObject(localVersion));
        var result = JsonConvert.DeserializeObject<Tuple<List<T>, List<T>, List<long>>>(json);//insert,update,delete
        if (result.Item1.Count == 0 && result.Item2.Count == 0 && result.Item3.Count == 0)
          return false;
        foreach (var item in result.Item1)
          session.Insert(item);
        foreach (var item in result.Item2)
          session.Update(item);
        if (result.Item3.Any())
          Delete<T>(session, result.Item3);
        session.Commit();
      }
      return true;
    }

    static void Delete<T>(IDmoSession session, List<long> ids)
      where T : BaseInfo
    {
      var delete = new DQDeleteDom(typeof(T));
      delete.Where.Conditions.Add(DQCondition.InList(DQExpression.Field("ID"), ids.Select(x => DQExpression.Value(x)).ToArray()));
      session.ExecuteNonQuery(delete);
    }

    static List<Tuple<long, int>> GetLocalVersion<T>(IDmoSession session)
      where T : BaseInfo
    {
      var query = new DQueryDom(new JoinAlias(typeof(T)));
      query.Columns.Add(DQSelectColumn.Field("ID"));
      query.Columns.Add(DQSelectColumn.Field("RowVersion"));
      return query.EExecuteList<long, int>(session);
    }

    public static bool SyncProductBatch(int batchType)
    {
      using (var session = DmoSession.New())
      {
        var local = GetLocalVersion<ProductBatch>(session);
        var json = RpcFacade.Call<string>(baseInfoRpcPath + "SyncProductBatchByType", batchType, JsonConvert.SerializeObject(local));

        var result = JsonConvert.DeserializeObject<Tuple<List<ProductBatch>, List<ProductBatch>, List<long>>>(json);//insert,update,delete
        if (result.Item1.Count == 0 && result.Item2.Count == 0 && result.Item3.Count == 0)
          return false;
        foreach (var item in result.Item1)
          session.Insert(item);
        foreach (var item in result.Item2)
          session.Update(item);
        if (result.Item3.Any())
          Delete<ProductBatch>(session, result.Item3);

        session.Commit();
      }
      return true;
    }

    public static bool SyncGoodsByTag(ApplyClient applyClient)
    {
      using (var session = DmoSession.New())
      {
        var local = GetLocalVersion<ClientGoodsSet>(session);
        var g2 = GetLocalVersion<Goods>(session);
        var json = RpcFacade.Call<string>(baseInfoRpcPath + "SyncClientGoodsSetByClient", (short)applyClient, JsonConvert.SerializeObject(local), JsonConvert.SerializeObject(g2));

        var result = JsonConvert.DeserializeObject<Tuple<List<ClientGoodsSet>, List<ClientGoodsSet>, List<long>, List<Goods>, List<Goods>, List<long>>>(json);//insert,update,delete
        if (result.Item1.Count == 0 && result.Item2.Count == 0 && result.Item3.Count == 0 && result.Item4.Count == 0 && result.Item5.Count == 0 && result.Item6.Count == 0)
          return false;
        foreach (var item in result.Item1)
          Insert(item, session);
        foreach (var item in result.Item2)
        {
          DeleteNotExistDetail(item, session);
          Update(item, session);
        }
        foreach (var item in result.Item3)
          DeleteClientSet(item, session);

        foreach (var item in result.Item4)
          session.Insert(item);
        foreach (var item in result.Item5)
          session.Update(item);
        if (result.Item6.Any())
          Delete<Goods>(session, result.Item6);

        session.Commit();
      }
      return true;
    }

    public static bool SyncGoodsIdentify(out List<GoodsIdentify> list) {
      using (var session = DmoSession.New())
      {
        var json = RpcFacade.Call<string>(baseInfoRpcPath + "SyncGoodsIdentify");
        list = JsonConvert.DeserializeObject<List<GoodsIdentify>>(json);
        var sql = "truncate table [Butcher_GoodsIdentify]";
        session.ExecuteSqlNonQuery(sql);

        foreach (var item in list)
        {
          session.Insert(item);
        }

        session.Commit();
      }
      return true;
    }

    static void DeleteClientSet(long id, IDmoSession session)
    {
      var detail = new DQDeleteDom(typeof(ClientGoodsSet_Detail));
      detail.Where.Conditions.Add(DQCondition.EQ("ClientGoodsSet_ID", id));
      session.ExecuteNonQuery(detail);

      var main = new DQDeleteDom(typeof(ClientGoodsSet));
      main.Where.Conditions.Add(DQCondition.EQ("ID", id));
      session.ExecuteNonQuery(main);
    }

    static void Insert(ClientGoodsSet entity, IDmoSession session)
    {
      session.Insert(entity);
      foreach (var detail in entity.Details)
        session.Insert(detail);
    }

    static void Update(ClientGoodsSet entity, IDmoSession session)
    {
      session.Update(entity);
      foreach (var detail in entity.Details)
        session.AddUpdateOrInsert(detail);
    }

    static void DeleteNotExistDetail(ClientGoodsSet entity, IDmoSession session)
    {
      var delete = new DQDeleteDom(typeof(ClientGoodsSet_Detail));
      delete.Where.Conditions.Add(DQCondition.And(DQCondition.EQ("ClientGoodsSet_ID", entity.ID), DQCondition.NotInList(DQExpression.Field("ID"), entity.Details.Select(x => DQExpression.Value(x.ID)).ToArray())));
      session.ExecuteNonQuery(delete);
    }
  }
}
