using System.Collections.Generic;
using System.Linq;
using Volpe.Cafe.MarketData.Design;

namespace Volpe.Cafe.MarketData.Parsers
{
    /// <summary>
    /// Provides methods for selecting a candidate leader vehicle for a component or a candidate component leader for a
    /// component family.
    /// </summary>
    public class LeaderSelection
    {
        /// <summary>
        /// Selects a candidate leader vehicle from a pool of the specified vehicles.
        /// </summary>
        /// <param name="vehicles">
        ///     A pre-filtered collection of vehicles from which to obtain a leader vehicle.
        /// </param>
        /// <returns>A candidate leader vehicle, or null if a leader could not be selected.</returns>
        /// <remarks>
        /// This method selects a leader by picking a vehicle with the lowest sales. In the event of a tie (if multiple
        /// vehicles have the same sales), the first vehicle, in chronological order, with the highest MSRP is selected
        /// as a leader.
        /// <br/><br/>
        /// <b>NOTE</b>:
        /// A leader vehicle is typically selected for a component or a component family. As such, all
        /// <paramref name="vehicles"/> in the input collection should belong to the same component or component
        /// family, depending on the criteria for which the leader is required to be obtained.
        /// </remarks>
        public static T SelectLeaderVehicle<T>(IEnumerable<T> vehicles) where T : IVehicleInfo
        {
            if (vehicles == null || vehicles.Count() == 0) {
                return default;
            }

            // when picking a leader, ignore ZEV candidates initially
            // if no vehicles remain, consider all vehicles instead
            var queryVehs = vehicles.Where(veh => veh.ZEVCandidate == null);
            if (queryVehs.Count() == 0) {
                queryVehs = vehicles;
            }

            // pick nameplate leader as follows:
            //  a) pick nameplate with highest total sales
            //  b) if a tie, pick nameplate with highest sales-weighted MSRP
            //  c) if still a tie, pick first nameplate in the list with highest sales and MSRP
            var nameplates = queryVehs.GroupBy(veh => veh.Nameplate);
            IGrouping<string, T> lNameplate = null;
            double lSales = int.MinValue;
            double lMSRP = int.MinValue;
            foreach (var np in nameplates) {
                double npSales = np.Sum(veh => veh.Sales);
                double npMSRP  = np.Sum(veh => veh.Msrp * veh.Sales) / npSales;

                if (npSales > lSales || (npSales == lSales && npMSRP > lMSRP)) {
                    lNameplate = np;
                    lSales = npSales;
                    lMSRP = npMSRP;
                }
            }

            // pick vehicle leader as follows:
            //  a) pick vehicle with higher sales as leader
            //  b) if sales match, pick vehicle with higher MSRP as leader
            T leader = default;
            lSales = int.MinValue;
            lMSRP = int.MinValue;
            foreach (T veh in lNameplate) {
                double vehSales = veh.Sales;
                double vehMSRP = veh.Msrp;

                if (vehSales > lSales || (vehSales == lSales && vehMSRP > lMSRP)) {
                    leader = veh;
                    lSales = vehSales;
                    lMSRP = vehMSRP;
                }
            }

            return leader;
        }

        /// <summary>
        /// Selects a candidate leader component from a pool of the specified components.
        /// </summary>
        /// <param name="components">
        ///     A pre-filtered collection of components from which to obtain a leader component.
        /// </param>
        /// <returns>A candidate leader component, or null if a leader could not be selected.</returns>
        /// <remarks>
        /// This method selects a leader by picking a component with the highest technology utilization. If multiple
        /// components have the same level of highest technology utilization, the component whose leader vehicle has
        /// the highest sales is selected. In the event of a tie (if multiple leader vehicles have the same sales), the
        /// first component, in chronological order, whose leader vehicle has the highest MSRP is selected as a family
        /// leader.
        /// <b>NOTE</b>:
        /// A leader component is typically selected for a component family. As such, all <paramref name="components"/>
        /// in the input collection should belong to the same component family, for which the leader is required to be
        /// obtained.
        /// </remarks>
        public static T SelectLeaderComponent<T>(IEnumerable<T> components) where T : IOEMComponent
        {
            if (components == null || components.Count() == 0) {
                return default;
            }

            // pick family leader as:
            //  a) component with highest technology utilization
            //  b) if multiple components, pick component where leader vehicle has higher sales
            //  c) if sales match, pick component where leader vehicle has higher MSRP

            var maxKey = components.Max(c => c.TechKey);
            var queryComponents = components.Where(c => c.TechKey == maxKey);

            T leader = default;
            double lSales = int.MinValue;
            double lMsrp = int.MinValue;

            foreach (T c in queryComponents) {
                var componentLeader = SelectLeaderVehicle(c.Vehicles.Select(veh => veh.RevisionInfo));
                if (componentLeader == null) {
                    continue;
                }

                double vehSales = componentLeader.Sales;
                double vehMsrp = componentLeader.Msrp;

                if (vehSales > lSales || (vehSales == lSales && vehMsrp > lMsrp)) {
                    leader = c;
                    lSales = vehSales;
                    lMsrp = vehMsrp;
                }
            }

            return leader;
        }

    }
}
