/*******************************************************************************
***  This file is part of Dux Rei Nummariae.                                 ***
***                                                                          ***
***  Copyright (C) 2017, 2018, 2019, 2020, 2021                              ***
***  John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Affero General Public License as published   ***
***  by the Free Software Foundation, either version 3 of the License, or    ***
***  (at your option) any later version.                                     ***
***                                                                          ***
***  This program is distributed in the hope that it will be useful, but     ***
***  WITHOUT ANY WARRANTY; without even the implied warranty of              ***
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    ***
***  See the GNU Affero General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Affero General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#include "BudgetHealth.h"
using ::QStringList;
using drn::adviser::BudgetHealth;
using drn::adviser::HealthStatuses;

#include <pecunia/Codes.h>
using pecunia::currency::minorUnitDigits;
using pecunia::currency::toStdString;
#include <pecunia/Math.h>
using pecunia::math::absoluteValue;
#include <QLocale>
using ::QLocale;
#include <QObject>
using ::QObject;
#include <QString>
using ::QString;

#include <cmath>
using std::abs;
#include <ostream>
using std::ostream;

#include <budgeting/Budget.h>
using drn::budgeting::BudgetTotals;
#include <foundation/EnumMacros.hpp>
// using BEGIN_ENUMERATION_TO_OTHER
// using MAP_ENUMERATION_TO_OTHER
// using END_ENUMERATION_TO_OTHER


const double BudgetHealth::lowerHealthyRange_{95.0};
const double BudgetHealth::lowerAilingRange_{87.5};
const double BudgetHealth::nontrackWageRatioThreshold_{0.05};

BudgetHealth::BudgetHealth() :
	percent_{100.0},
	status_{HealthStatuses::Healthy},
	information_{}
{}

BudgetHealth::BudgetHealth(const BudgetTotals& totals) :
	percent_{
		[&totals] ()
		{
			const auto totalGoingOut{
				totals.goals_ + totals.bills_ + totals.debts_ + totals.nontrack_
			};
			const auto leftOver{totals.wage_ - totalGoingOut};

			if (totals.wage_ > 0 && leftOver > 0)
				return (totalGoingOut / totals.wage_) * 100.0;

			if (leftOver < 0)
				return (totals.wage_ / totalGoingOut) * 100.0;
			return 100.0;
		}()
	},
	status_{
		[this] ()
		{
			if (this->percent_ >= BudgetHealth::lowerHealthyRange_)
				return HealthStatuses::Healthy;
			else if (
				this->percent_ >= BudgetHealth::lowerAilingRange_
				&& this->percent_ < BudgetHealth::lowerHealthyRange_
			)
				return HealthStatuses::Ailing;
			else
				return HealthStatuses::Sick;
		}()
	},
	information_{
		[&totals] ()
		{
			QStringList info{};
			const auto totalGoingOut{
				totals.goals_ + totals.bills_ + totals.debts_ + totals.nontrack_
			};
			const auto leftOver{totals.wage_ - totalGoingOut};
			const auto decimalPoint{QLocale{}.decimalPoint()};

			if (totals.wage_ > 0 && leftOver > 0)
				info << QObject::tr(
							"There is %1 left over every month (%2% of monthly wages are budgeted)."
						)
							.arg(
								QString{"%1 %2%3%4"}
									.arg(QString::fromStdString(toStdString(leftOver.code())))
									.arg(leftOver.major())
									.arg(decimalPoint)
									.arg(
										leftOver.minor(),
										minorUnitDigits(leftOver.code()),
										10,
										QChar{'0'}
									)
							)
							.arg((totalGoingOut / totals.wage_) * 100.0, 0, 'f', 1);
			else if (totals.wage_ > 0 && leftOver < 0)
				info << QObject::tr(
							"There is %1 more needed every month (a deficit of %2% of monthly "
								"wages budgeted)."
						)
							.arg(
								QString{"%1 %2%3%4"}
									.arg(QString::fromStdString(toStdString(leftOver.code())))
									.arg(abs(leftOver.major()))
									.arg(decimalPoint)
									.arg(
										leftOver.minor(),
										minorUnitDigits(leftOver.code()),
										10,
										QChar{'0'}
									)
							)
							.arg((absoluteValue(leftOver) / totals.wage_) * 100.0, 0, 'f', 1);
			else if (leftOver < 0)
				info << QObject::tr(
							"There are no wages budgeted in the month, but %1 is budgeted every "
								"month (a deficit of 100.0% of monthly wages budgeted)."
						)
							.arg(
								QString{"%1 %2%3%4"}
									.arg(QString::fromStdString(toStdString(leftOver.code())))
									.arg(abs(leftOver.major()))
									.arg(decimalPoint)
									.arg(
										leftOver.minor(),
										minorUnitDigits(leftOver.code()),
										10,
										QChar{'0'}
									)
							);
			else
				info << QObject::tr("The entire wages budgeted are used every month.");

			if (
				(totals.wage_ == 0 && totals.nontrack_ > 0)
				|| (
					totals.wage_ != 0
					&& totals.nontrack_ / totals.wage_
						> BudgetHealth::nontrackWageRatioThreshold_
				)
			)
				info << QObject::tr(
					"The nontrack spending is greater than %1% of the monthly wages."
				).arg(BudgetHealth::nontrackWageRatioThreshold_ * 100.0, 0, 'f', 1);
			return info;
		}()
	}
{}

const double& BudgetHealth::percent() const
{
	return this->percent_;
}

const HealthStatuses& BudgetHealth::status() const
{
	return this->status_;
}

const QStringList& BudgetHealth::information() const
{
	return this->information_;
}

ostream& drn::adviser::operator<<(ostream& o, const HealthStatuses& hs)
{
	return o << "Value (" << presentationText(hs).toStdString() << ')';
}

QString drn::adviser::presentationText(const HealthStatuses& hs)
{
	BEGIN_ENUMERATION_TO_OTHER(hs);
		MAP_ENUMERATION_TO_OTHER(HealthStatuses::Healthy, "Healthy");
		MAP_ENUMERATION_TO_OTHER(HealthStatuses::Ailing, "Ailing");
		MAP_ENUMERATION_TO_OTHER(HealthStatuses::Sick, "Sick");
	END_ENUMERATION_TO_OTHER(hs, HealthStatuses);
}
