﻿namespace Localwire.PhotographerCalendar.Core.Order
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Customer;
    using Service;

    public class Order : DomainModel
    {
        public const int MinimumYear = 2010;
        public readonly Service OriginatingService;
        private readonly Dictionary<Guid, OrderPayment> _customerPayments = new Dictionary<Guid, OrderPayment>();

        private Customer _customer;
        private DateTime _realizationDate;
        private double _finalPrice;

        public Order(DateTime realizationDate, Service originatingService, Customer customer, Guid? id = null, DateTime? creationDate = null) : base(id, creationDate)
        {
            RealizationDate = realizationDate;
            if (originatingService == null)
                throw new InvalidOperationException($"Attempt to set null {nameof(OriginatingService)}");
            OriginatingService = originatingService;
            Customer = customer;
            FinalPrice = OriginatingService.Price;
        }

        public double FinalPrice
        {
            get { return _finalPrice; }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException($"Attempt to set non-positive {nameof(FinalPrice)}");
                if (Math.Abs(value - _finalPrice) < 0.001) return;
                _finalPrice = value;
            }
        }

        public Customer Customer
        {
            get { return _customer; }
            set
            {
                if (_customer != null && _customer.Equals(value)) return;
                if (value == null)
                    throw new InvalidOperationException($"Attempt to set empty {nameof(Customer)}");
                _customer = value;
            }
        }

        public DateTime RealizationDate
        {
            get { return _realizationDate; }
            set
            {
                if (value == _realizationDate) return;
                if (value.Year < MinimumYear)
                    throw new ArgumentOutOfRangeException("value", $"Attempt to set {nameof(RealizationDate)} with year before {MinimumYear}");
                _realizationDate = value;
            }
        }

        public double Balance => _finalPrice - _customerPayments.Values.Where(p => p != null).Sum(p => p.Amount);
        public IEnumerable<OrderPayment> CustomerPayments => _customerPayments.Values.ToList();

        public OrderPayment AddPayment(double amount, DateTime? date = null, Guid? id = null, DateTime? creationDate = null)
        {
            var payment = new OrderPayment(this, amount, id, creationDate);
            if (date.HasValue)
                payment.PaymentDate = date.Value;
            _customerPayments.Add(payment.Id, payment);
            return payment;
        }

        public void RemovePayment(OrderPayment payment)
        {
            if (payment == null)
                throw new ArgumentNullException(nameof(payment));
            if (!_customerPayments.ContainsKey(payment.Id))
                throw new InvalidOperationException($"{nameof(CustomerPayments)} does not contain such payment");
            _customerPayments.Remove(payment.Id);
        }
    }
}