#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do)

using System;

using Volpe.Cafe.EffectsModels;
using Volpe.Cafe.Logging;
using Volpe.Cafe.MarketData.Design;
using Volpe.Cafe.ModelSettings;

namespace Volpe.Cafe.DfsSrModels.SalesEstimators
{
    /// <inheritdoc cref="SalesEstimator.PriceElasticity"/>
    [Serializable]
    public sealed class PriceElasticitySalesEstimator : SalesEstimatorBase
    {
        /// <summary>
        /// Initializes a new instances of the <see cref="PriceElasticitySalesEstimator"/> class, using the specified
        /// industry data, modeling settings, log writer, and reference sales estimator.
        /// </summary>
        /// <param name="referenceSalesEstimator">
        ///     The reference estimator to use for obtaining the sales projections attributed to the forecast fleet
        ///     from the baseline scenario. If not specified, this value will default to a <see cref="FixedForecast"/>
        ///     sales estimator.
        /// </param>
        /// <inheritdoc cref="SalesEstimatorBase(IIndustry, ModelingSettings, ILogger)" path="/param"/>
        /// <inheritdoc cref="SalesEstimatorBase(IIndustry, ModelingSettings, ILogger)" path="/exception"/>
        public PriceElasticitySalesEstimator(
            IIndustry data, ModelingSettings settings, ILogger logWriter, ISalesEstimator referenceSalesEstimator)
            : base(data, settings, logWriter)
        {
            this._refSalesEstimator = referenceSalesEstimator ?? new FixedForecast(data, settings, logWriter);
        }

        private ISalesEstimator _refSalesEstimator;

        public override double CalcNewLightDutySales(
            Scenario scen, int iteration, int year, ForecastFleet baseFleet, ForecastFleet scenFleet)
        {
            double refSales = this._refSalesEstimator.CalcNewLightDutySales(scen, iteration, year, baseFleet, scenFleet);
            var baseFleetData = baseFleet.LightDutyForecastData;
            var scenFleetData = scenFleet.LightDutyForecastData;
            var vehStyles = DfsSrHelpers.VehStylesLightDuty;

            return this.CalcNewSales(scen, year, baseFleetData, scenFleetData, vehStyles, refSales);
        }

        public override double CalcNewHdpuvSales(
            Scenario scen, int iteration, int year, ForecastFleet baseFleet, ForecastFleet scenFleet)
        {
            double refSales = this._refSalesEstimator.CalcNewHdpuvSales(scen, iteration, year, baseFleet, scenFleet);
            var baseFleetData = baseFleet.HdpuvForecastData;
            var scenFleetData = scenFleet.HdpuvForecastData;
            var vehStyles = DfsSrHelpers.VehStylesHdpuv;

            return this.CalcNewSales(scen, year, baseFleetData, scenFleetData, vehStyles, refSales);
        }

        private double CalcNewSales(
            Scenario scen, int year, ForecastFleetData baseFleetData, ForecastFleetData scenFleetData,
            VehicleStyle[] vehStyles, double refSales)
        {
            if (scen.IsBaseline) {
                return refSales;
            }

            double baseRegCost = baseFleetData.RegCost;
            double scenRegCost = scenFleetData.RegCost;
            double deltaRegCost = scenRegCost - baseRegCost;

            double baseTaxCredit = baseFleetData.TaxCredit;
            double scenTaxCredit = scenFleetData.TaxCredit;
            double deltaTaxCredit = scenTaxCredit - baseTaxCredit;

            double baseBatteryTaxCredit = baseFleetData.BatteryTaxCredit;
            double scenBatteryTaxCredit = scenFleetData.BatteryTaxCredit;
            double deltaBatteryTaxCredit = scenBatteryTaxCredit - baseBatteryTaxCredit;

            FuelValue baseFE = baseFleetData.OnRoadFE;
            FuelValue baseFS = baseFleetData.FuelShare;
            FuelValue scenFE = scenFleetData.OnRoadFE;
            FuelValue scenFS = scenFleetData.FuelShare;
            double fuelSavings = Effects.CalcAssumedFuelSavings(year, baseFE, baseFS, scenFE, scenFS, this.Settings);

            double priceMultiplier = this.Settings.RuntimeSettings.PriceElasticitySalesEstimatorMultiplier;
            double scenNetBenefits = deltaRegCost - deltaTaxCredit - deltaBatteryTaxCredit - fuelSavings;
            double baseRetailPrice = CalcHistoricAveragePrice(this.MinYear - 1)
                                   + baseRegCost - baseTaxCredit - baseBatteryTaxCredit;
            double salesDelta = scenNetBenefits * priceMultiplier * refSales / baseRetailPrice;

            return refSales + salesDelta;

            // local helper functions
            double CalcHistoricAveragePrice(int hYear)
            {
                var hfd = this.Settings.Parameters.HistoricFleetData;
                double hPrice = 0;
                double hSales = 0;

                foreach (var vehStyle in vehStyles) {
                    var fleetData = hfd.GetHistoricFleetData(vehStyle, hYear);
                    hPrice += fleetData.InitialProduction * fleetData.TransactionPrice;
                    hSales += fleetData.InitialProduction;
                }
                return hPrice / hSales;
            }
        }

    }
}
