﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.ActiveRecord;
namespace Hugg.Logic
{
    /// <summary>
    /// This class represents a subscription that was bought by a client
    /// </summary>
    [ActiveRecord("SubscriptionsOfClients")]
    public class SubscriptionOfClient : ActiveRecordBase<SubscriptionOfClient>
    {
        /// <summary>
        /// Empty constructor
        /// </summary>
        protected SubscriptionOfClient()
        { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="client">the customer</param>
        /// <param name="type">type of subscription that was bought</param>
        public SubscriptionOfClient(Customer client, SubscriptionType type)
        {
            Client = client;
            BoughtDate = DateTime.Now;
            TypeOfSubscription = type;

            LessonsLeft = type.ClassesInSub.NumOfLessons;


        }
        /// <summary>
        /// Id of subscription that was bought
        /// </summary>
        [PrimaryKey]
        public int IdSubscription
        {
            get;
            private set;
        }

        /// <summary>
        /// The customer
        /// </summary>
        [BelongsTo("IdClient")]
        public Customer Client
        {
            get;
            private set;
        }
        /// <summary>
        /// The date in which the subscription was bought
        /// </summary>
        [Property]
        public DateTime BoughtDate
        {
            get;
            private set;
        }

        /// <summary>
        /// Type of subscription that was bought
        /// </summary>
        [BelongsTo("SubType")]
        public SubscriptionType TypeOfSubscription
        {
            get;
            private set;
        }

        /// <summary>
        /// Lessons left in the subscription
        /// </summary>
        [Property]
        public int LessonsLeft
        {
            get;
            internal set;
        }


        /// <summary>
        /// This method returns the subscription of client by the specified id
        /// </summary>
        /// <param name="id">id of subscription of client</param>
        /// <returns>the subscription of client by the specified id</returns>
        /// <exception cref=" LogicExceptions.DatabaseException"> 
        /// thrown when any kind of exception is thrown by the database layer
        /// </exception>
        public static SubscriptionOfClient GetSubscriptionById(int id)
        {
            try
            {
                return (SubscriptionOfClient)ActiveRecordBase.FindByPrimaryKey(typeof(SubscriptionOfClient), id);
            }
            catch (Exception)
            {
                throw new LogicExceptions.DatabaseException();
            }
        }


        /// <summary>
        /// This method creates the subscription of client
        /// </summary>
        /// <param name="client">the customer</param>
        /// <param name="typeOfSubscription">type of subscription to buy</param>
        /// <returns>the new subscription of client</returns>
        /// <exception cref=" LogicExceptions.DatabaseException"> 
        /// thrown when any kind of exception is thrown by the database layer
        /// </exception>
        /// <exception cref=" LogicExceptions.SubscriptionTypeDoesntExistException"> 
        /// thrown when the specified subscription type doesn't exist or is canceled
        /// </exception>
        public static SubscriptionOfClient CreateSubscriptionOfClient(Customer client, SubscriptionType typeOfSubscription)
        {

            if (typeOfSubscription == null || typeOfSubscription.Canceled == true)
                throw new LogicExceptions.SubscriptionTypeDoesntExistException("Subscription type in subscription of client doesn't exist");

            SubscriptionOfClient subscription = new SubscriptionOfClient(client, typeOfSubscription);
            if (!subscription.AlreadyExist())
            {
                try
                {
                    subscription.Create();
                }
                catch (Exception)
                {
                    throw new LogicExceptions.DatabaseException();
                }
            }

            return subscription;


        }

        /// <summary>
        /// This method checks if the the subscription of client already exists in the system
        /// </summary>
        /// <returns>true if exists, false otherwise</returns>
        private bool AlreadyExist()
        {
            try
            {
                SubscriptionOfClient[] allSubscriptions = (SubscriptionOfClient[])ActiveRecordBase.FindAll(typeof(SubscriptionOfClient));
                IEnumerable<SubscriptionOfClient> matchingSubscriptions =
                    allSubscriptions.Where(sub =>
                        ((sub.Client == this.Client) && (sub.BoughtDate == this.BoughtDate) && (sub.TypeOfSubscription == this.TypeOfSubscription)
                        && (sub.LessonsLeft == this.LessonsLeft)));
                if (matchingSubscriptions == null || !matchingSubscriptions.Any())
                    return false;
                else
                    return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// This method returns all the subscriptions of the specified client
        /// </summary>
        /// <param name="client">the customer</param>
        /// <returns>all the subscriptions of the specified client</returns>
        /// <exception cref=" LogicExceptions.DatabaseException"> 
        /// thrown when any kind of exception is thrown by the database layer
        /// </exception>
        public static SubscriptionOfClient[] GetAllSubscriptionsOfClient(Customer client)
        {
            try
            {
                return (SubscriptionOfClient[])ActiveRecordBase.FindAllByProperty(typeof(SubscriptionOfClient), "Client", client);
            }
            catch (Exception)
            {
                throw new LogicExceptions.DatabaseException();
            }
        }



        /// <summary>
        /// This method returns the greatest number of remaining lessons of the specified client in all of
        /// his subscriptions 
        /// </summary>
        /// <param name="client">the customer</param>
        /// <returns>the greatest number of remaining lessons of the specified client in all of
        /// his subscriptions 
        /// <exception cref=" LogicExceptions.DatabaseException"> 
        /// thrown when any kind of exception is thrown by the database layer
        /// </exception>
        /// <exception cref=" LogicExceptions.NoSubscriptionsToClientException"> 
        /// thrown when the client doesn't have any subscriptions
        /// </exception>
        public static int? GetMaxIfEnds(Customer client)
        {
            try
            {
                SubscriptionOfClient[] sub = (SubscriptionOfClient[])ActiveRecordBase.FindAllByProperty(typeof(SubscriptionOfClient), "Client", client);
                if (sub.Length != 0)
                {
                    IEnumerable<SubscriptionOfClient> aboutToEnd = from s in sub
                                                                   orderby s.LessonsLeft descending
                                                                   select s;

                    if (aboutToEnd == null || !aboutToEnd.Any())
                    {
                        return null;
                    }

                    SubscriptionOfClient max = (SubscriptionOfClient)(aboutToEnd.First());
                    return max.LessonsLeft;
                }
                else
                    throw new LogicExceptions.NoSubscriptionsToClientException();
            }
            catch (LogicExceptions.NoSubscriptionsToClientException e)
            {
                throw e;
            }
            catch (Exception)
            {
                throw new LogicExceptions.DatabaseException();
            }
        }

        /// <summary>
        /// This method returns the subscription of the specified client 
        /// in which he can register to the specified lesson type
        /// </summary>
        /// <param name="client">the customer</param>
        /// <param name="classType">lesson type</param>
        /// <returns>the subscription of the specified client 
        /// in which he can register to the specified lesson type</returns>
        /// <exception cref=" LogicExceptions.DatabaseException"> 
        /// thrown when any kind of exception is thrown by the database layer
        /// </exception>
        public static SubscriptionOfClient GetFreeSubscription(Customer client, TypeClass classType)
        {
            SubscriptionOfClient[] sclient = GetAllSubscriptionsOfClient(client);
            if (sclient.Length > 0)
            {

                foreach (SubscriptionOfClient s in sclient) //first take from specifiec type
                {
                    SubscriptionType subscriptionType = s.TypeOfSubscription;

                    bool cond2 = ((subscriptionType.ClassesInSub is OneClassesInSubscription) &&
                       ((subscriptionType.ClassesInSub as OneClassesInSubscription).ClassOfSubscription.Equals(classType)));

                    if (s.LessonsLeft > 0 && cond2)
                        return s;
                }

                foreach (SubscriptionOfClient s in sclient)
                {
                    SubscriptionType subscriptionType = s.TypeOfSubscription;

                    bool cond1 = (subscriptionType.ClassesInSub is AllClassesInSubscription);

                    if (s.LessonsLeft > 0 && cond1)
                        return s;
                }
            }
            return null;
        }

        /// <summary>
        /// This method returns the hash code
        /// </summary>
        /// <returns>the hash code</returns>
        public override int GetHashCode()
        {
            return this.IdSubscription;
        }

        /// <summary>
        /// This method returns true if the two subscriptions of client are equal
        /// </summary>
        /// <param name="obj">subscription of client to be compared to</param>
        /// <returns>true if the two subscriptions of client are equal (by their id), false otherwise</returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            if (!(obj is SubscriptionOfClient))
                return false;
            return ((obj as SubscriptionOfClient).IdSubscription == this.IdSubscription);
        }

        /// <summary>
        /// This method adds lesson credit to the specified subscription of client
        /// </summary>
        /// <param name="subscriptionOfClient">subscription of client</param>
        /// <exception cref=" LogicExceptions.DatabaseException"> 
        /// thrown when any kind of exception is thrown by the database layer
        /// </exception>
        public static void AddLessonCreditToSub(SubscriptionOfClient subscriptionOfClient)
        {
            try
            {

                subscriptionOfClient.LessonsLeft++;
                subscriptionOfClient.Update();
            }
            catch (Exception)
            {
                throw new LogicExceptions.DatabaseException();
            }
        }


    }




}

