﻿using System.Linq;
using NuClear.Replication.Core;
using NuClear.Replication.Core.DataObjects;
using NuClear.Replication.Core.Equality;
using NuClear.ValidationRules.Replication.Commands;
using Facts = NuClear.ValidationRules.Storage.Model.Facts;
using Order = NuClear.ValidationRules.Storage.Model.Aggregates.PriceRules.Order;

namespace NuClear.ValidationRules.Replication.PriceRules.Aggregates
{
    public sealed class OrderAggregateRootActor : AggregateRootActor<Order>
    {
        public OrderAggregateRootActor(
            IQuery query,
            IEqualityComparerFactory equalityComparerFactory,
            IRepository<Order> bulkRepository,
            IRepository<Order.OrderPricePosition> orderPricePositionBulkRepository)
            : base(query, equalityComparerFactory)
        {
            HasRootEntity(new OrderAccessor(query), bulkRepository,
                HasValueObject(new OrderPricePositionAccessor(query), orderPricePositionBulkRepository));
        }

        public sealed class OrderAccessor : DataChangesHandler<Order>, IStorageBasedDataObjectAccessor<Order>
        {
            private readonly IQuery _query;

            public OrderAccessor(IQuery query) : base(CreateInvalidator()) => _query = query;

            private static IRuleInvalidator CreateInvalidator()
            {
                return new RuleInvalidator
                {
                    // все проверки исключены из пересчёта в масссовом режиме, события ничего не изменят.
                    // {MessageTypeCode.OrderMustHaveActualPrice},
                    // {MessageTypeCode.OrderPositionCorrespondToInactivePosition},
                    // {MessageTypeCode.OrderPositionMustCorrespondToActualPrice},
                };
            }

            public IQueryable<Order> GetSource() =>
                from order in _query.For<Facts::Order>()
                from orderWorkflow in _query.For<Facts::OrderWorkflow>().Where(x => x.Id == order.Id)
                let price = _query.For<Facts::Price>()
                    .Where(x => x.ProjectId == order.ProjectId)
                    .Where(x => x.BeginDate <= order.Start)
                    .OrderByDescending(x => x.BeginDate)
                    .FirstOrDefault()
                select new Order
                {
                    Id = order.Id,
                    Start = order.Start,
                    End = order.FactEnd,
                    IsCommitted = Facts::OrderWorkflowStep.Committed.Contains(orderWorkflow.Step),
                    ActualPriceId = price != null ? (long?)price.Id : null
                };

            public FindSpecification<Order> GetFindSpecification(ICommand command)
            {
                var aggregateIds = ((SyncDataObjectCommand)command).DataObjectIds;
                return new FindSpecification<Order>(x => aggregateIds.Contains(x.Id));
            }
        }

        public sealed class OrderPricePositionAccessor : DataChangesHandler<Order.OrderPricePosition>, IStorageBasedDataObjectAccessor<Order.OrderPricePosition>
        {
            private readonly IQuery _query;

            public OrderPricePositionAccessor(IQuery query) : base(new RuleInvalidator()) => _query = query;

            public IQueryable<Order.OrderPricePosition> GetSource() =>
                from orderPosition in _query.For<Facts::OrderPosition>()
                join pricePosition in _query.For<Facts::PricePosition>() on orderPosition.PricePositionId equals pricePosition.Id
                select new Order.OrderPricePosition
                   {
                       OrderId = orderPosition.OrderId,
                       OrderPositionId = orderPosition.Id,
                       PositionId = pricePosition.PositionId,

                       PriceId = pricePosition.PriceId,
                       IsActive = pricePosition.IsActiveNotDeleted,
                   };

            public FindSpecification<Order.OrderPricePosition> GetFindSpecification(ICommand command)
            {
                var aggregateIds = ((ReplaceValueObjectCommand)command).AggregateRootIds;
                return new FindSpecification<Order.OrderPricePosition>(x => aggregateIds.Contains(x.OrderId));
            }
        }
    }
}
