/*
 * Mirai. Copyright (C) 2024 Vyn
 * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
 * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
 */

#include "TasksView.h"
#include "Mirai.h"
#include "TaskItem.h"
#include "utils.h"
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <ctime>
#include <format>
#include <iostream>
#include <memory>
#include <ostream>
#include <stdexcept>
#include <string>
#include <vector>

namespace mirai
{

TasksView::TasksView(Mirai *miraiInstance) : mirai(miraiInstance)
{
	if (!miraiInstance) {
		throw std::runtime_error("NULL pointer passed in TasksView");
	}
	update();
}

FilteredDay &TasksView::operator[](int index)
{
	return filteredDays.at(index);
}

size_t TasksView::count() const
{
	return filteredDays.size();
}

void TasksView::update()
{
	auto todayDate = std::format("{:%Y-%m-%d}", std::chrono::system_clock::now());
	filteredDays.clear();

	for (auto &file : mirai->getSources()) {
		if (resourcesFilter.size() > 0 &&
			!vectorUtils::contains(resourcesFilter, file->getName())) {
			continue;
		}
		for (auto &day : *file->days()) {
			FilteredDay filteredDay{.day = day.get()};
			for (auto &task : *day->tasks()) {
				auto taskDate = std::format(
					"{:04d}-{:02d}-{:02d}", day->getDate().year, day->getDate().month,
					day->getDate().day
				);
				if (shouldHideCompletedTasks() && task->getState() == DONE &&
					taskDate < todayDate) {
					continue;
				}
				if (tagsFilter.size() > 0 &&
					!vectorUtils::containsAll(tagsFilter, task->getTags())) {
					continue;
				}
				filteredDay.filteredTasks.push_back(task.get());
			}
			for (auto &event : *day->events()) {
				FilteredEvent filteredEvent{.event = event.get()};
				auto eventDate = std::format(
					"{:04d}-{:02d}-{:02d}", day->getDate().year, day->getDate().month,
					day->getDate().day
				);
				for (auto &task : *event->tasks()) {

					if (shouldHideCompletedTasks() && task->getState() == DONE &&
						eventDate < todayDate) {
						continue;
					}
					if (tagsFilter.size() > 0 &&
						!vectorUtils::containsAll(tagsFilter, task->getTags())) {
						continue;
					}
					filteredEvent.filteredTasks.push_back(task.get());
				}
				if (!filteredEvent.filteredTasks.empty() || !shouldHideCompletedTasks() ||
					eventDate >= todayDate) {
					filteredDay.filteredEvents.push_back(filteredEvent);
				}
			}
			if (!filteredDay.filteredEvents.empty() || !filteredDay.filteredTasks.empty()) {
				auto existingDay = std::ranges::find_if(filteredDays, [&](const FilteredDay &date) {
					return date.day->getDate() == filteredDay.day->getDate();
				});
				if (existingDay == filteredDays.end()) {
					filteredDays.push_back(filteredDay);
				} else {
					existingDay->filteredTasks.insert(
						existingDay->filteredTasks.end(), filteredDay.filteredTasks.begin(),
						filteredDay.filteredTasks.end()
					);
					existingDay->filteredEvents.insert(
						existingDay->filteredEvents.end(), filteredDay.filteredEvents.begin(),
						filteredDay.filteredEvents.end()
					);
				}
			}
		}

		for (auto &filteredDay : filteredDays) {
			std::ranges::sort(
				filteredDay.filteredEvents,
				[](const FilteredEvent &t1, const FilteredEvent &t2) {
					return t1.event->getStartTime().hour < t2.event->getStartTime().hour;
				}
			);
		}

		std::ranges::sort(filteredDays, [](const FilteredDay &t1, const FilteredDay &t2) {
			return t1.day->getDate() < t2.day->getDate();
		});
	}
}

void TasksView::addTagFilter(const std::string &tag)
{
	tagsFilter.push_back(tag);
	update();
}

void TasksView::removeTagFilter(const std::string &tag)
{
	tagsFilter
		.erase(std::remove_if(tagsFilter.begin(), tagsFilter.end(), [&](const auto &tagInFilter) {
			return tag == tagInFilter;
		}));

	update();
}

void TasksView::addSourceFilter(const std::string &fileName)
{
	resourcesFilter.push_back(fileName);
	update();
}

void TasksView::removeSourceFilter(const std::string &fileName)
{
	resourcesFilter.erase(std::remove_if(
		resourcesFilter.begin(), resourcesFilter.end(),
		[&](const auto &fileInFilter) {
			return fileName == fileInFilter;
		}
	));
	update();
}

void TasksView::removeFilters()
{
	tagsFilter.clear();
	update();
}

const std::vector<std::string> &TasksView::getActiveTagsFilter()
{
	return tagsFilter;
}

const std::vector<std::string> &TasksView::getActiveFilesFilter()
{
	return resourcesFilter;
}

void TasksView::hideCompletedTasks(bool hide)
{
	shouldHideCompletedTasks_ = hide;
}

bool TasksView::shouldHideCompletedTasks() const
{
	return shouldHideCompletedTasks_;
}
} // namespace mirai
