/*******************************************************************************
***  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 <QDate>
using ::QDate;
#include <QObject>
using ::QObject;
// using Q_OBJECT
#include <qt-mocks/Matchers.h>
using qt_mocks::equals;
// using QVERIFY_THAT;
// using QVERIFY_EXCEPTION_THROWN;
#include <QTest>
// using QTEST_MAIN
#include <vector>
using std::vector;
#include <pecunia/Codes.h>
using pecunia::currency::Iso4217Codes;
#include <pecunia/Money.h>
using pecunia::currency::Money;

#include <budgeting/Bill.h>
using drn::budgeting::Bill;
#include <budgeting/Budget.h>
using drn::budgeting::Budget;
#include <budgeting/BudgetItemDue.h>
using drn::budgeting::BudgetItemDue;
using drn::budgeting::budgetItemsDue;
#include <budgeting/BudgetSource.h>
using drn::budgeting::BudgetSource;
#include <budgeting/EventFrequency.h>
using drn::budgeting::EventFrequency;
#include <budgeting/Debt.h>
using drn::budgeting::Debt;
using drn::budgeting::Percentage;
#include <budgeting/Goal.h>
using drn::budgeting::Goal;
#include <budgeting/Nontrack.h>
using drn::budgeting::Nontrack;
#include <budgeting/Wage.h>
using drn::budgeting::Wage;
#include <foundation/CalendarTime.hpp>
using drn::foundation::Days;


namespace drn
{
namespace budgeting
{
namespace tests
{

struct BudgetItemsDueUT : QObject
{
	Q_OBJECT

public:
	BudgetItemsDueUT() :
		b_{},
		water_{
			BudgetSource{"Water"},
			Money{321, 5400u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6}
		},
		electricity_{
			BudgetSource{"Electricity"},
			Money{123, 4500u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6}
		},
		visa_{
			BudgetSource{"Visa"},
			Money{15, 1200u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6},
			Money{12345, 1200u, Iso4217Codes::USD},
			Percentage{40.5}
		},
		masterCard_{
			BudgetSource{"MasterCard"},
			Money{45, 1200u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6},
			Money{54321, 2100u, Iso4217Codes::USD},
			Percentage{20.5}
		},
		computer_{
			BudgetSource{"Computer"},
			Money{45, 1200u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6},
			Money{958, 2400u, Iso4217Codes::USD}
		},
		car_{
			BudgetSource{"Car"},
			Money{125, 2500u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6},
			Money{20123, 5900u, Iso4217Codes::USD}
		},
		wife_{
			BudgetSource{"Wife"},
			Money{453, 7400u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6}
		},
		husband_{
			BudgetSource{"Husband"},
			Money{752, 7600u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6}
		},
		mcDonalds_{
			BudgetSource{"McDonald's"},
			Money{125, 2500u, Iso4217Codes::USD},
			EventFrequency::Weekly,
			{9999, 12, 6}
		},
		burgerKing_{
			BudgetSource{"Burger King"},
			Money{175, 5000u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			{9999, 12, 6}
		},
		dueWithin_{7}
	{}

	QDate addToToday(const Days& add) const
	{
		const auto adjusted{QDate::currentDate().addDays(add.count())};
		return adjusted;
	}

	Budget b_;
	Bill water_;
	Bill electricity_;
	Debt visa_;
	Debt masterCard_;
	Goal computer_;
	Goal car_;
	Nontrack wife_;
	Nontrack husband_;
	Wage mcDonalds_;
	Wage burgerKing_;
	Days dueWithin_;

private slots:
	void cleanup()
	{
		this->b_ = {};
	}

	void hasNoItemsDue_ShouldBeEmpty()
	{
		this->b_.bills_.add(this->water_);
		this->b_.bills_.add(this->electricity_);
		this->b_.debts_.add(this->visa_);
		this->b_.debts_.add(this->masterCard_);
		this->b_.goals_.add(this->computer_);
		this->b_.goals_.add(this->car_);
		this->b_.nontracks_.add(this->wife_);
		this->b_.nontracks_.add(this->husband_);
		this->b_.wages_.add(this->mcDonalds_);
		this->b_.wages_.add(this->burgerKing_);
		const auto actual{budgetItemsDue(this->dueWithin_, this->b_)};
		const vector<BudgetItemDue> expected{};
		QVERIFY_THAT(actual, equals(expected));
	}

	void hasItemsDue_ShouldBeItemsDue()
	{
		const Bill water{
			BudgetSource{"Water"},
			Money{321, 5400u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			this->addToToday(this->dueWithin_)
		};
		this->b_.bills_.add(water);
		this->b_.bills_.add(this->electricity_);
		const Debt visa{
			BudgetSource{"Visa"},
			Money{15, 1200u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			this->addToToday(this->dueWithin_ / 2),
			Money{12345, 1200u, Iso4217Codes::USD},
			Percentage{40.5}
		};
		this->b_.debts_.add(visa);
		this->b_.debts_.add(this->masterCard_);
		this->b_.goals_.add(this->computer_);
		const Goal car{
			BudgetSource{"Car"},
			Money{125, 2500u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			this->addToToday(this->dueWithin_ / 4),
			Money{20123, 5900u, Iso4217Codes::USD}
		};
		this->b_.goals_.add(car);
		const Nontrack wife{
			BudgetSource{"Wife"},
			Money{453, 7400u, Iso4217Codes::USD},
			EventFrequency::Monthly,
			this->addToToday(Days{0})
		};
		this->b_.nontracks_.add(wife);
		this->b_.nontracks_.add(this->husband_);
		const Wage mcDonalds{
			BudgetSource{"McDonald's"},
			Money{125, 2500u, Iso4217Codes::USD},
			EventFrequency::Weekly,
			this->addToToday(Days{-1})
		};
		this->b_.wages_.add(mcDonalds);
		this->b_.wages_.add(this->burgerKing_);
		const auto actual{budgetItemsDue(this->dueWithin_, this->b_)};
		const vector<BudgetItemDue> expected{
			{
				BudgetItemDue{
					{BudgetItemTypes::Wage, mcDonalds.source()},
					mcDonalds.amount(),
					mcDonalds.nextOccurOn()
				},
				BudgetItemDue{
					{BudgetItemTypes::Nontrack, wife.source()},
					wife.amount(),
					wife.nextOccurOn()
				},
				BudgetItemDue{
					{BudgetItemTypes::Goal, car.source()},
					car.amount(),
					car.nextOccurOn()
				},
				BudgetItemDue{
					{BudgetItemTypes::Debt, visa.source()},
					visa.amount(),
					visa.nextOccurOn()
				},
				BudgetItemDue{
					{BudgetItemTypes::Bill, water.source()},
					water.amount(),
					water.nextOccurOn()
				}
			}
		};
		QVERIFY_THAT(actual, equals(expected));
	}
};


}}}

QTEST_MAIN(drn::budgeting::tests::BudgetItemsDueUT)
#include "BudgetItemsDue-unit-tests.moc"
